Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -64,7 +64,6 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 64 |
"--no-zygote",
|
| 65 |
],
|
| 66 |
)
|
| 67 |
-
|
| 68 |
self.persistent_context = await self.browser.new_context(
|
| 69 |
user_agent=(
|
| 70 |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
@@ -76,7 +75,7 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 76 |
await self.persistent_context.add_init_script(
|
| 77 |
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 78 |
)
|
| 79 |
-
|
| 80 |
setup_page = await self.persistent_context.new_page()
|
| 81 |
try:
|
| 82 |
print("[SERVER] Opening duck.ai for setup...")
|
|
@@ -98,10 +97,197 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 98 |
finally:
|
| 99 |
await setup_page.close()
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
async def _chat(self, model_label: str, prompt: str) -> str:
|
| 102 |
page = await self.persistent_context.new_page()
|
| 103 |
try:
|
| 104 |
-
# !! timeout ูุงุญุฏ ูุจูุฑ ููู ุงูุตูุญุฉ โ ูุง ููุบูููุฑ ุฃุจุฏุงู
|
| 105 |
page.set_default_timeout(180000)
|
| 106 |
|
| 107 |
# โโ 1. ูุชุญ ุงูุตูุญุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -125,155 +311,25 @@ class AsyncBrowserThread(threading.Thread):
|
|
| 125 |
print("[DUCK] Input ready โ")
|
| 126 |
|
| 127 |
# โโ 4. ุชุบููุฑ ุงููู
ูุฐุฌ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 128 |
-
|
| 129 |
-
model_btn = page.locator('button[data-testid="model-select-button"]')
|
| 130 |
-
current_text = await model_btn.inner_text()
|
| 131 |
-
print(f"[DUCK] Current model: {current_text.strip()}")
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
await asyncio.sleep(2)
|
| 136 |
-
|
| 137 |
-
option = page.locator(
|
| 138 |
-
f"li:has-text('{model_label}'), "
|
| 139 |
-
f"[role='option']:has-text('{model_label}')"
|
| 140 |
-
)
|
| 141 |
-
if await option.count() > 0:
|
| 142 |
-
await option.first.click()
|
| 143 |
-
print(f"[DUCK] Model โ {model_label} โ")
|
| 144 |
-
else:
|
| 145 |
-
await page.keyboard.press("Escape")
|
| 146 |
-
print("[DUCK] Model not found, using default")
|
| 147 |
-
|
| 148 |
-
await asyncio.sleep(3)
|
| 149 |
-
await page.wait_for_selector(
|
| 150 |
-
'textarea[name="user-prompt"]', state="visible"
|
| 151 |
-
)
|
| 152 |
-
print("[DUCK] Textarea re-confirmed โ")
|
| 153 |
-
|
| 154 |
-
except Exception as e:
|
| 155 |
-
print(f"[DUCK] Model select (non-fatal): {e}")
|
| 156 |
-
|
| 157 |
-
# โโ 5. ุญูู ุงููุต ุนุจุฑ JavaScript โโโโโโโโโโโโโโโโโโโโโโโโ
|
| 158 |
-
await page.evaluate(
|
| 159 |
-
"""
|
| 160 |
-
(text) => {
|
| 161 |
-
const ta = document.querySelector('textarea[name="user-prompt"]');
|
| 162 |
-
if (!ta) return;
|
| 163 |
-
const nativeSetter = Object.getOwnPropertyDescriptor(
|
| 164 |
-
window.HTMLTextAreaElement.prototype, 'value'
|
| 165 |
-
).set;
|
| 166 |
-
nativeSetter.call(ta, text);
|
| 167 |
-
ta.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
| 168 |
-
ta.dispatchEvent(new Event('change', { bubbles: true }));
|
| 169 |
-
ta.focus();
|
| 170 |
-
}
|
| 171 |
-
""",
|
| 172 |
-
prompt
|
| 173 |
-
)
|
| 174 |
-
await asyncio.sleep(2)
|
| 175 |
-
|
| 176 |
-
# โโ 6. ุฅุฑุณุงู ุงูุฑุณุงูุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 177 |
-
# ู
ุญุงููุฉ 1: ุฒุฑ Submit
|
| 178 |
-
sent = False
|
| 179 |
-
try:
|
| 180 |
-
send_btn = page.locator('button[type="submit"][aria-label="Send"]')
|
| 181 |
-
is_disabled = await send_btn.get_attribute("disabled")
|
| 182 |
-
if is_disabled is None:
|
| 183 |
-
await send_btn.click()
|
| 184 |
-
print(f"[DUCK] Sent via button โ")
|
| 185 |
-
sent = True
|
| 186 |
-
except Exception:
|
| 187 |
-
pass
|
| 188 |
-
|
| 189 |
-
# ู
ุญุงููุฉ 2: Enter ู
ุจุงุดุฑุฉ
|
| 190 |
if not sent:
|
| 191 |
-
|
| 192 |
-
await ta.click()
|
| 193 |
-
await asyncio.sleep(0.5)
|
| 194 |
-
await page.keyboard.press("Enter")
|
| 195 |
-
print(f"[DUCK] Sent via Enter โ")
|
| 196 |
-
|
| 197 |
-
print(f"[DUCK] Prompt length: {len(prompt)} chars")
|
| 198 |
|
| 199 |
-
# โโ
|
| 200 |
-
await
|
| 201 |
-
response_started = False
|
| 202 |
-
try:
|
| 203 |
-
stop_btn = page.locator('button[aria-label="Stop generating"]')
|
| 204 |
-
await stop_btn.wait_for(state="visible", timeout=30000)
|
| 205 |
-
print("[DUCK] Response started โ")
|
| 206 |
-
response_started = True
|
| 207 |
-
except Exception:
|
| 208 |
-
print("[DUCK] Stop btn not detected, watching for content...")
|
| 209 |
-
|
| 210 |
-
# โโ 8. ุงูุชุธุงุฑ ุงูุชู
ุงู ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 211 |
-
max_wait = 150
|
| 212 |
-
elapsed = 0
|
| 213 |
-
|
| 214 |
-
if response_started:
|
| 215 |
-
while elapsed < max_wait:
|
| 216 |
-
await asyncio.sleep(2)
|
| 217 |
-
elapsed += 2
|
| 218 |
-
still_running = await page.locator(
|
| 219 |
-
'button[aria-label="Stop generating"]:not([disabled])'
|
| 220 |
-
).count()
|
| 221 |
-
if still_running == 0:
|
| 222 |
-
print(f"[DUCK] Complete (~{elapsed}s) โ")
|
| 223 |
-
break
|
| 224 |
-
else:
|
| 225 |
-
# ุงูุชุธุฑ ุธููุฑ article
|
| 226 |
-
while elapsed < max_wait:
|
| 227 |
-
await asyncio.sleep(2)
|
| 228 |
-
elapsed += 2
|
| 229 |
-
if await page.locator('article').count() > 0:
|
| 230 |
-
await asyncio.sleep(6)
|
| 231 |
-
print(f"[DUCK] Article appeared (~{elapsed}s) โ")
|
| 232 |
-
break
|
| 233 |
-
|
| 234 |
-
await asyncio.sleep(2)
|
| 235 |
-
|
| 236 |
-
# โโ 9. ุงุณุชุฎุฑุงุฌ ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 237 |
-
response_text = await page.evaluate("""
|
| 238 |
-
() => {
|
| 239 |
-
const articles = document.querySelectorAll('article');
|
| 240 |
-
if (articles.length > 0) {
|
| 241 |
-
return articles[articles.length - 1].innerText.trim();
|
| 242 |
-
}
|
| 243 |
-
const msgDivs = document.querySelectorAll(
|
| 244 |
-
'[class*="message"]:not([class*="user"])' +
|
| 245 |
-
':not([class*="User"]):not([class*="input"])'
|
| 246 |
-
);
|
| 247 |
-
if (msgDivs.length > 0) {
|
| 248 |
-
const valid = [...msgDivs].filter(el =>
|
| 249 |
-
el.innerText && el.innerText.trim().length > 20 &&
|
| 250 |
-
!el.querySelector('textarea')
|
| 251 |
-
);
|
| 252 |
-
if (valid.length > 0) {
|
| 253 |
-
return valid[valid.length - 1].innerText.trim();
|
| 254 |
-
}
|
| 255 |
-
}
|
| 256 |
-
const divs = [...document.querySelectorAll('div')].filter(el =>
|
| 257 |
-
el.children.length < 10 &&
|
| 258 |
-
el.innerText && el.innerText.trim().length > 50 &&
|
| 259 |
-
!el.querySelector('textarea') &&
|
| 260 |
-
!el.querySelector('button[type="submit"]')
|
| 261 |
-
);
|
| 262 |
-
if (divs.length > 0) {
|
| 263 |
-
return divs.sort(
|
| 264 |
-
(a, b) => b.innerText.length - a.innerText.length
|
| 265 |
-
)[0].innerText.trim();
|
| 266 |
-
}
|
| 267 |
-
return '';
|
| 268 |
-
}
|
| 269 |
-
""")
|
| 270 |
|
| 271 |
-
|
|
|
|
| 272 |
await asyncio.sleep(5)
|
| 273 |
response_text = await page.evaluate("""
|
| 274 |
() => {
|
| 275 |
const arts = document.querySelectorAll('article');
|
| 276 |
if (arts.length > 0) return arts[arts.length-1].innerText.trim();
|
|
|
|
|
|
|
| 277 |
return document.body.innerText.slice(0, 5000);
|
| 278 |
}
|
| 279 |
""")
|
|
|
|
| 64 |
"--no-zygote",
|
| 65 |
],
|
| 66 |
)
|
|
|
|
| 67 |
self.persistent_context = await self.browser.new_context(
|
| 68 |
user_agent=(
|
| 69 |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
|
|
| 75 |
await self.persistent_context.add_init_script(
|
| 76 |
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 77 |
)
|
| 78 |
+
# ูุจูู ุงูุดุฑูุท ู
ุฑุฉ ูุงุญุฏุฉ
|
| 79 |
setup_page = await self.persistent_context.new_page()
|
| 80 |
try:
|
| 81 |
print("[SERVER] Opening duck.ai for setup...")
|
|
|
|
| 97 |
finally:
|
| 98 |
await setup_page.close()
|
| 99 |
|
| 100 |
+
async def _select_model(self, page, model_label: str):
|
| 101 |
+
"""
|
| 102 |
+
ุชุบููุฑ ุงููู
ูุฐุฌ ุนุจุฑ:
|
| 103 |
+
1. Ctrl+Alt+M ููุชุญ ูุงุฆู
ุฉ ุงููู
ุงุฐุฌ
|
| 104 |
+
2. ุงุฎุชูุงุฑ ุงููู
ูุฐุฌ
|
| 105 |
+
3. ุงูุถุบุท ุนูู "Start chat"
|
| 106 |
+
"""
|
| 107 |
+
try:
|
| 108 |
+
model_btn = page.locator('button[data-testid="model-select-button"]')
|
| 109 |
+
current_text = (await model_btn.inner_text()).strip()
|
| 110 |
+
print(f"[DUCK] Current model: {current_text}")
|
| 111 |
+
|
| 112 |
+
if model_label.lower() in current_text.lower():
|
| 113 |
+
print(f"[DUCK] Model already correct โ")
|
| 114 |
+
return True
|
| 115 |
+
|
| 116 |
+
# ูุชุญ ูุงุฆู
ุฉ ุงููู
ุงุฐุฌ
|
| 117 |
+
await model_btn.click()
|
| 118 |
+
await asyncio.sleep(2)
|
| 119 |
+
|
| 120 |
+
# ุงูุจุญุซ ุนู ุงููู
ูุฐุฌ ูู ุงููุงุฆู
ุฉ
|
| 121 |
+
# ู
ู HTML: ูุงุฆู
ุฉ ูููุง ุงุณู
ุงููู
ูุฐุฌ ููุต
|
| 122 |
+
option = page.locator(
|
| 123 |
+
f'li:has-text("{model_label}"), '
|
| 124 |
+
f'[role="option"]:has-text("{model_label}"), '
|
| 125 |
+
f'button:has-text("{model_label}")'
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
if await option.count() > 0:
|
| 129 |
+
await option.first.click()
|
| 130 |
+
print(f"[DUCK] Model selected: {model_label} โ")
|
| 131 |
+
await asyncio.sleep(1)
|
| 132 |
+
|
| 133 |
+
# ุงูุจุญุซ ุนู ุฒุฑ "Start chat" ุจุนุฏ ุงุฎุชูุงุฑ ุงููู
ูุฐุฌ
|
| 134 |
+
start_btn = page.locator(
|
| 135 |
+
'button:has-text("Start chat"), '
|
| 136 |
+
'button:has-text("Start new chat")'
|
| 137 |
+
)
|
| 138 |
+
if await start_btn.count() > 0:
|
| 139 |
+
await start_btn.first.click()
|
| 140 |
+
print("[DUCK] Start chat clicked โ")
|
| 141 |
+
await asyncio.sleep(2)
|
| 142 |
+
|
| 143 |
+
# ุงูุชุธุฑ textarea ุจุนุฏ ุงูุชุบููุฑ
|
| 144 |
+
await page.wait_for_selector(
|
| 145 |
+
'textarea[name="user-prompt"]',
|
| 146 |
+
state="visible",
|
| 147 |
+
timeout=15000
|
| 148 |
+
)
|
| 149 |
+
print("[DUCK] Textarea ready after model change โ")
|
| 150 |
+
return True
|
| 151 |
+
else:
|
| 152 |
+
await page.keyboard.press("Escape")
|
| 153 |
+
print(f"[DUCK] Model '{model_label}' not in list, using default")
|
| 154 |
+
return False
|
| 155 |
+
|
| 156 |
+
except Exception as e:
|
| 157 |
+
print(f"[DUCK] Model select error (non-fatal): {e}")
|
| 158 |
+
return False
|
| 159 |
+
|
| 160 |
+
async def _inject_and_send(self, page, prompt: str) -> bool:
|
| 161 |
+
"""
|
| 162 |
+
ุญูู ุงููุต ูู textarea ูุฅุฑุณุงูู
|
| 163 |
+
"""
|
| 164 |
+
# ุญูู ุงููุต ุนุจุฑ JavaScript ู
ุน React events
|
| 165 |
+
await page.evaluate(
|
| 166 |
+
"""
|
| 167 |
+
(text) => {
|
| 168 |
+
const ta = document.querySelector('textarea[name="user-prompt"]');
|
| 169 |
+
if (!ta) return;
|
| 170 |
+
const nativeSetter = Object.getOwnPropertyDescriptor(
|
| 171 |
+
window.HTMLTextAreaElement.prototype, 'value'
|
| 172 |
+
).set;
|
| 173 |
+
nativeSetter.call(ta, text);
|
| 174 |
+
ta.dispatchEvent(new InputEvent('input', { bubbles: true }));
|
| 175 |
+
ta.dispatchEvent(new Event('change', { bubbles: true }));
|
| 176 |
+
ta.focus();
|
| 177 |
+
}
|
| 178 |
+
""",
|
| 179 |
+
prompt
|
| 180 |
+
)
|
| 181 |
+
await asyncio.sleep(2)
|
| 182 |
+
|
| 183 |
+
# ู
ุญุงููุฉ 1: ุฒุฑ Submit
|
| 184 |
+
try:
|
| 185 |
+
send_btn = page.locator('button[type="submit"][aria-label="Send"]')
|
| 186 |
+
is_disabled = await send_btn.get_attribute("disabled")
|
| 187 |
+
if is_disabled is None:
|
| 188 |
+
await send_btn.click()
|
| 189 |
+
print(f"[DUCK] Sent via button โ ({len(prompt)} chars)")
|
| 190 |
+
return True
|
| 191 |
+
except Exception:
|
| 192 |
+
pass
|
| 193 |
+
|
| 194 |
+
# ู
ุญุงููุฉ 2: Enter ุนูู ุงูู textarea
|
| 195 |
+
try:
|
| 196 |
+
ta = page.locator('textarea[name="user-prompt"]')
|
| 197 |
+
await ta.click()
|
| 198 |
+
await asyncio.sleep(0.3)
|
| 199 |
+
await page.keyboard.press("Enter")
|
| 200 |
+
print(f"[DUCK] Sent via Enter โ ({len(prompt)} chars)")
|
| 201 |
+
return True
|
| 202 |
+
except Exception as e:
|
| 203 |
+
print(f"[DUCK] Send failed: {e}")
|
| 204 |
+
return False
|
| 205 |
+
|
| 206 |
+
async def _wait_for_response(self, page) -> str:
|
| 207 |
+
"""
|
| 208 |
+
ุงูุชุธุงุฑ ุงูุชู
ุงู ุงูุฑุฏ ูุงุณุชุฎุฑุงุฌู
|
| 209 |
+
"""
|
| 210 |
+
# ุงูุชุธุฑ ุธููุฑ ุงูุฑุฏ
|
| 211 |
+
await asyncio.sleep(3)
|
| 212 |
+
|
| 213 |
+
# ุงูุชุธุฑ ุฒุฑ Copy ุจู data-copyairesponse="true"
|
| 214 |
+
# ูุฐุง ูุธูุฑ ููุท ุจุนุฏ ุงูุชู
ุงู ุงูุฑุฏ ุงูุฃุฎูุฑ
|
| 215 |
+
try:
|
| 216 |
+
copy_btn = page.locator('button[data-copyairesponse="true"]')
|
| 217 |
+
await copy_btn.last.wait_for(state="visible", timeout=90000)
|
| 218 |
+
print("[DUCK] Response complete (copy button appeared) โ")
|
| 219 |
+
await asyncio.sleep(1)
|
| 220 |
+
except Exception:
|
| 221 |
+
print("[DUCK] Copy button not detected, using fallback wait...")
|
| 222 |
+
# fallback: ุงูุชุธุฑ ุงุฎุชูุงุก Stop button
|
| 223 |
+
max_wait = 120
|
| 224 |
+
elapsed = 0
|
| 225 |
+
while elapsed < max_wait:
|
| 226 |
+
await asyncio.sleep(2)
|
| 227 |
+
elapsed += 2
|
| 228 |
+
stop_active = await page.locator(
|
| 229 |
+
'button[aria-label="Stop generating"]:not([disabled])'
|
| 230 |
+
).count()
|
| 231 |
+
if stop_active == 0:
|
| 232 |
+
print(f"[DUCK] Response complete (stop gone ~{elapsed}s) โ")
|
| 233 |
+
break
|
| 234 |
+
|
| 235 |
+
await asyncio.sleep(1)
|
| 236 |
+
|
| 237 |
+
# ุงุณุชุฎุฑุงุฌ ุงููุต ู
ู ุงูุจููุฉ ุงูุญููููุฉ
|
| 238 |
+
# ู
ู HTML: div[data-activeresponse="true"] > ... > div.space-y-4
|
| 239 |
+
response_text = await page.evaluate("""
|
| 240 |
+
() => {
|
| 241 |
+
// ุทุฑููุฉ 1: div[data-activeresponse="true"] โ ุงูุจููุฉ ุงูุญููููุฉ ูู duck.ai
|
| 242 |
+
const activeResp = document.querySelector('[data-activeresponse="true"]');
|
| 243 |
+
if (activeResp) {
|
| 244 |
+
// ุงููุต ูู div.space-y-4 ุฏุงุฎู ุงูุฑุฏ
|
| 245 |
+
const textDiv = activeResp.querySelector('.space-y-4');
|
| 246 |
+
if (textDiv && textDiv.innerText.trim().length > 0) {
|
| 247 |
+
return textDiv.innerText.trim();
|
| 248 |
+
}
|
| 249 |
+
// ุฃู ุงููุต ุงูู
ุจุงุดุฑ
|
| 250 |
+
const prose = activeResp.querySelector(
|
| 251 |
+
'[class*="whitespace-normal"], [class*="prose"]'
|
| 252 |
+
);
|
| 253 |
+
if (prose && prose.innerText.trim().length > 0) {
|
| 254 |
+
return prose.innerText.trim();
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
// ุทุฑููุฉ 2: ุขุฎุฑ div[data-activeresponse]
|
| 259 |
+
const allResps = document.querySelectorAll('[data-activeresponse]');
|
| 260 |
+
if (allResps.length > 0) {
|
| 261 |
+
const last = allResps[allResps.length - 1];
|
| 262 |
+
const textDiv = last.querySelector('.space-y-4');
|
| 263 |
+
if (textDiv) return textDiv.innerText.trim();
|
| 264 |
+
return last.innerText.trim();
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
// ุทุฑููุฉ 3: article
|
| 268 |
+
const articles = document.querySelectorAll('article');
|
| 269 |
+
if (articles.length > 0) {
|
| 270 |
+
return articles[articles.length - 1].innerText.trim();
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
// ุทุฑููุฉ 4: div.space-y-4 ุงูุฃุฎูุฑ
|
| 274 |
+
const spaceDivs = document.querySelectorAll('.space-y-4');
|
| 275 |
+
if (spaceDivs.length > 0) {
|
| 276 |
+
for (let i = spaceDivs.length - 1; i >= 0; i--) {
|
| 277 |
+
const text = spaceDivs[i].innerText.trim();
|
| 278 |
+
if (text.length > 10) return text;
|
| 279 |
+
}
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
return '';
|
| 283 |
+
}
|
| 284 |
+
""")
|
| 285 |
+
|
| 286 |
+
return response_text.strip()
|
| 287 |
+
|
| 288 |
async def _chat(self, model_label: str, prompt: str) -> str:
|
| 289 |
page = await self.persistent_context.new_page()
|
| 290 |
try:
|
|
|
|
| 291 |
page.set_default_timeout(180000)
|
| 292 |
|
| 293 |
# โโ 1. ูุชุญ ุงูุตูุญุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 311 |
print("[DUCK] Input ready โ")
|
| 312 |
|
| 313 |
# โโ 4. ุชุบููุฑ ุงููู
ูุฐุฌ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 314 |
+
await self._select_model(page, model_label)
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
+
# โโ 5. ุฅุฑุณุงู ุงูุฑุณุงูุฉ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 317 |
+
sent = await self._inject_and_send(page, prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
if not sent:
|
| 319 |
+
raise RuntimeError("Failed to send message")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
|
| 321 |
+
# โโ 6. ุงูุชุธุงุฑ ูุงุณุชุฎุฑุงุฌ ุงูุฑุฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 322 |
+
response_text = await self._wait_for_response(page)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
|
| 324 |
+
# fallback ุฅุฐุง ูุงู ูุงุฑุบุงู
|
| 325 |
+
if not response_text or len(response_text.strip()) < 5:
|
| 326 |
await asyncio.sleep(5)
|
| 327 |
response_text = await page.evaluate("""
|
| 328 |
() => {
|
| 329 |
const arts = document.querySelectorAll('article');
|
| 330 |
if (arts.length > 0) return arts[arts.length-1].innerText.trim();
|
| 331 |
+
const sp = document.querySelectorAll('.space-y-4');
|
| 332 |
+
if (sp.length > 0) return sp[sp.length-1].innerText.trim();
|
| 333 |
return document.body.innerText.slice(0, 5000);
|
| 334 |
}
|
| 335 |
""")
|