Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -505,11 +505,24 @@ async def interact_screenshot(request: SessionRequest, auth=Depends(verify_token
|
|
| 505 |
driver = session['driver']
|
| 506 |
|
| 507 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
screenshot = driver.get_screenshot_as_png()
|
| 509 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
| 510 |
|
| 511 |
return {
|
| 512 |
"screenshot": screenshot_b64,
|
|
|
|
| 513 |
"url": driver.current_url,
|
| 514 |
"title": driver.title
|
| 515 |
}
|
|
@@ -517,35 +530,87 @@ async def interact_screenshot(request: SessionRequest, auth=Depends(verify_token
|
|
| 517 |
logger.error(f"[INTERACT-SCREENSHOT] Erro: {e}")
|
| 518 |
raise HTTPException(status_code=500, detail=str(e))
|
| 519 |
|
|
|
|
| 520 |
@app.post("/interact/click")
|
| 521 |
async def interact_click(request: InteractClickRequest, auth=Depends(verify_token)):
|
| 522 |
-
"""Clica em coordenada específica na página"""
|
| 523 |
session = session_manager.get_session(request.session_id)
|
| 524 |
driver = session['driver']
|
| 525 |
|
| 526 |
try:
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
actions.reset_actions()
|
| 530 |
-
|
| 531 |
-
# Usar JavaScript para clicar na posição exata
|
| 532 |
-
driver.execute_script(f"""
|
| 533 |
-
var element = document.elementFromPoint({request.x}, {request.y});
|
| 534 |
-
if (element) {{
|
| 535 |
-
element.click();
|
| 536 |
-
element.focus();
|
| 537 |
-
}}
|
| 538 |
-
""")
|
| 539 |
|
| 540 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
|
| 542 |
screenshot = driver.get_screenshot_as_png()
|
| 543 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
| 544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
return {
|
| 546 |
"status": "clicked",
|
| 547 |
"x": request.x,
|
| 548 |
"y": request.y,
|
|
|
|
| 549 |
"screenshot": screenshot_b64,
|
| 550 |
"url": driver.current_url,
|
| 551 |
"title": driver.title
|
|
@@ -554,22 +619,47 @@ async def interact_click(request: InteractClickRequest, auth=Depends(verify_toke
|
|
| 554 |
logger.error(f"[INTERACT-CLICK] Erro: {e}")
|
| 555 |
raise HTTPException(status_code=500, detail=str(e))
|
| 556 |
|
|
|
|
| 557 |
@app.post("/interact/type")
|
| 558 |
async def interact_type(request: InteractTypeRequest, auth=Depends(verify_token)):
|
| 559 |
-
"""Digita texto no elemento focado"""
|
| 560 |
session = session_manager.get_session(request.session_id)
|
| 561 |
driver = session['driver']
|
| 562 |
|
| 563 |
try:
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
|
| 574 |
time.sleep(0.3)
|
| 575 |
|
|
@@ -578,7 +668,7 @@ async def interact_type(request: InteractTypeRequest, auth=Depends(verify_token)
|
|
| 578 |
|
| 579 |
return {
|
| 580 |
"status": "typed",
|
| 581 |
-
"text":
|
| 582 |
"screenshot": screenshot_b64,
|
| 583 |
"url": driver.current_url,
|
| 584 |
"title": driver.title
|
|
@@ -587,9 +677,10 @@ async def interact_type(request: InteractTypeRequest, auth=Depends(verify_token)
|
|
| 587 |
logger.error(f"[INTERACT-TYPE] Erro: {e}")
|
| 588 |
raise HTTPException(status_code=500, detail=str(e))
|
| 589 |
|
|
|
|
| 590 |
@app.post("/interact/key")
|
| 591 |
async def interact_key(request: InteractKeyRequest, auth=Depends(verify_token)):
|
| 592 |
-
"""Pressiona tecla especial"""
|
| 593 |
session = session_manager.get_session(request.session_id)
|
| 594 |
driver = session['driver']
|
| 595 |
|
|
@@ -612,9 +703,10 @@ async def interact_key(request: InteractKeyRequest, auth=Depends(verify_token)):
|
|
| 612 |
raise HTTPException(status_code=400, detail=f"Tecla não suportada: {request.key}")
|
| 613 |
|
| 614 |
actions = ActionChains(driver)
|
| 615 |
-
actions.send_keys(key)
|
|
|
|
| 616 |
|
| 617 |
-
time.sleep(0.
|
| 618 |
|
| 619 |
screenshot = driver.get_screenshot_as_png()
|
| 620 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
|
@@ -632,6 +724,7 @@ async def interact_key(request: InteractKeyRequest, auth=Depends(verify_token)):
|
|
| 632 |
logger.error(f"[INTERACT-KEY] Erro: {e}")
|
| 633 |
raise HTTPException(status_code=500, detail=str(e))
|
| 634 |
|
|
|
|
| 635 |
@app.post("/interact/scroll")
|
| 636 |
async def interact_scroll(request: SessionRequest, auth=Depends(verify_token)):
|
| 637 |
"""Rola a página para baixo"""
|
|
@@ -655,6 +748,7 @@ async def interact_scroll(request: SessionRequest, auth=Depends(verify_token)):
|
|
| 655 |
logger.error(f"[INTERACT-SCROLL] Erro: {e}")
|
| 656 |
raise HTTPException(status_code=500, detail=str(e))
|
| 657 |
|
|
|
|
| 658 |
@app.post("/interact/clear-and-type")
|
| 659 |
async def interact_clear_and_type(request: InteractTypeRequest, auth=Depends(verify_token)):
|
| 660 |
"""Limpa o campo focado e digita novo texto"""
|
|
@@ -662,16 +756,18 @@ async def interact_clear_and_type(request: InteractTypeRequest, auth=Depends(ver
|
|
| 662 |
driver = session['driver']
|
| 663 |
|
| 664 |
try:
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
|
|
|
|
|
|
| 675 |
|
| 676 |
time.sleep(0.3)
|
| 677 |
|
|
@@ -690,6 +786,7 @@ async def interact_clear_and_type(request: InteractTypeRequest, auth=Depends(ver
|
|
| 690 |
raise HTTPException(status_code=500, detail=str(e))
|
| 691 |
|
| 692 |
|
|
|
|
| 693 |
# ============================================================
|
| 694 |
# ROTAS DE BUSCA NO SITE
|
| 695 |
# ============================================================
|
|
|
|
| 505 |
driver = session['driver']
|
| 506 |
|
| 507 |
try:
|
| 508 |
+
# Pegar tamanho real do viewport
|
| 509 |
+
viewport = driver.execute_script("""
|
| 510 |
+
return {
|
| 511 |
+
width: window.innerWidth,
|
| 512 |
+
height: window.innerHeight,
|
| 513 |
+
scrollX: window.scrollX,
|
| 514 |
+
scrollY: window.scrollY,
|
| 515 |
+
pageWidth: document.documentElement.scrollWidth,
|
| 516 |
+
pageHeight: document.documentElement.scrollHeight
|
| 517 |
+
};
|
| 518 |
+
""")
|
| 519 |
+
|
| 520 |
screenshot = driver.get_screenshot_as_png()
|
| 521 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
| 522 |
|
| 523 |
return {
|
| 524 |
"screenshot": screenshot_b64,
|
| 525 |
+
"viewport": viewport,
|
| 526 |
"url": driver.current_url,
|
| 527 |
"title": driver.title
|
| 528 |
}
|
|
|
|
| 530 |
logger.error(f"[INTERACT-SCREENSHOT] Erro: {e}")
|
| 531 |
raise HTTPException(status_code=500, detail=str(e))
|
| 532 |
|
| 533 |
+
|
| 534 |
@app.post("/interact/click")
|
| 535 |
async def interact_click(request: InteractClickRequest, auth=Depends(verify_token)):
|
| 536 |
+
"""Clica em coordenada específica na página usando ActionChains"""
|
| 537 |
session = session_manager.get_session(request.session_id)
|
| 538 |
driver = session['driver']
|
| 539 |
|
| 540 |
try:
|
| 541 |
+
x = request.x
|
| 542 |
+
y = request.y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 543 |
|
| 544 |
+
# Método 1: ActionChains com offset desde o body
|
| 545 |
+
try:
|
| 546 |
+
body = driver.find_element(By.TAG_NAME, 'body')
|
| 547 |
+
actions = ActionChains(driver)
|
| 548 |
+
actions.move_to_element_with_offset(body, x - body.size['width'] // 2, y - body.size['height'] // 2)
|
| 549 |
+
actions.click()
|
| 550 |
+
actions.perform()
|
| 551 |
+
except Exception:
|
| 552 |
+
# Método 2: JavaScript com elementFromPoint + click + focus
|
| 553 |
+
driver.execute_script("""
|
| 554 |
+
var x = arguments[0];
|
| 555 |
+
var y = arguments[1];
|
| 556 |
+
|
| 557 |
+
// Scroll para a posição se necessário
|
| 558 |
+
var vpHeight = window.innerHeight;
|
| 559 |
+
if (y > vpHeight) {
|
| 560 |
+
window.scrollTo(0, y - vpHeight / 2);
|
| 561 |
+
y = vpHeight / 2;
|
| 562 |
+
}
|
| 563 |
+
|
| 564 |
+
var element = document.elementFromPoint(x, y);
|
| 565 |
+
if (element) {
|
| 566 |
+
// Simular eventos de mouse reais
|
| 567 |
+
var events = ['mousedown', 'mouseup', 'click'];
|
| 568 |
+
events.forEach(function(eventType) {
|
| 569 |
+
var event = new MouseEvent(eventType, {
|
| 570 |
+
view: window,
|
| 571 |
+
bubbles: true,
|
| 572 |
+
cancelable: true,
|
| 573 |
+
clientX: x,
|
| 574 |
+
clientY: y
|
| 575 |
+
});
|
| 576 |
+
element.dispatchEvent(event);
|
| 577 |
+
});
|
| 578 |
+
|
| 579 |
+
// Focar no elemento
|
| 580 |
+
if (element.focus) element.focus();
|
| 581 |
+
|
| 582 |
+
// Se for input/textarea, colocar cursor no final
|
| 583 |
+
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
| 584 |
+
element.selectionStart = element.selectionEnd = element.value.length;
|
| 585 |
+
}
|
| 586 |
+
}
|
| 587 |
+
""", x, y)
|
| 588 |
+
|
| 589 |
+
time.sleep(0.8)
|
| 590 |
|
| 591 |
screenshot = driver.get_screenshot_as_png()
|
| 592 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
| 593 |
|
| 594 |
+
# Verificar qual elemento está focado
|
| 595 |
+
focused = driver.execute_script("""
|
| 596 |
+
var el = document.activeElement;
|
| 597 |
+
if (el) {
|
| 598 |
+
return {
|
| 599 |
+
tag: el.tagName,
|
| 600 |
+
type: el.type || '',
|
| 601 |
+
id: el.id || '',
|
| 602 |
+
name: el.name || '',
|
| 603 |
+
isInput: (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable)
|
| 604 |
+
};
|
| 605 |
+
}
|
| 606 |
+
return null;
|
| 607 |
+
""")
|
| 608 |
+
|
| 609 |
return {
|
| 610 |
"status": "clicked",
|
| 611 |
"x": request.x,
|
| 612 |
"y": request.y,
|
| 613 |
+
"focused_element": focused,
|
| 614 |
"screenshot": screenshot_b64,
|
| 615 |
"url": driver.current_url,
|
| 616 |
"title": driver.title
|
|
|
|
| 619 |
logger.error(f"[INTERACT-CLICK] Erro: {e}")
|
| 620 |
raise HTTPException(status_code=500, detail=str(e))
|
| 621 |
|
| 622 |
+
|
| 623 |
@app.post("/interact/type")
|
| 624 |
async def interact_type(request: InteractTypeRequest, auth=Depends(verify_token)):
|
| 625 |
+
"""Digita texto no elemento focado usando ActionChains"""
|
| 626 |
session = session_manager.get_session(request.session_id)
|
| 627 |
driver = session['driver']
|
| 628 |
|
| 629 |
try:
|
| 630 |
+
text = request.text
|
| 631 |
+
|
| 632 |
+
# Método 1: ActionChains (simula digitação real tecla por tecla)
|
| 633 |
+
try:
|
| 634 |
+
actions = ActionChains(driver)
|
| 635 |
+
actions.send_keys(text)
|
| 636 |
+
actions.perform()
|
| 637 |
+
except Exception:
|
| 638 |
+
# Método 2: JavaScript direto
|
| 639 |
+
driver.execute_script("""
|
| 640 |
+
var text = arguments[0];
|
| 641 |
+
var el = document.activeElement;
|
| 642 |
+
if (el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable)) {
|
| 643 |
+
if (el.isContentEditable) {
|
| 644 |
+
el.textContent += text;
|
| 645 |
+
} else {
|
| 646 |
+
var start = el.selectionStart || el.value.length;
|
| 647 |
+
var end = el.selectionEnd || el.value.length;
|
| 648 |
+
var before = el.value.substring(0, start);
|
| 649 |
+
var after = el.value.substring(end);
|
| 650 |
+
el.value = before + text + after;
|
| 651 |
+
el.selectionStart = el.selectionEnd = start + text.length;
|
| 652 |
+
}
|
| 653 |
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
| 654 |
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
| 655 |
+
// KeyUp para frameworks reativos
|
| 656 |
+
text.split('').forEach(function(char) {
|
| 657 |
+
el.dispatchEvent(new KeyboardEvent('keydown', { key: char, bubbles: true }));
|
| 658 |
+
el.dispatchEvent(new KeyboardEvent('keypress', { key: char, bubbles: true }));
|
| 659 |
+
el.dispatchEvent(new KeyboardEvent('keyup', { key: char, bubbles: true }));
|
| 660 |
+
});
|
| 661 |
+
}
|
| 662 |
+
""", text)
|
| 663 |
|
| 664 |
time.sleep(0.3)
|
| 665 |
|
|
|
|
| 668 |
|
| 669 |
return {
|
| 670 |
"status": "typed",
|
| 671 |
+
"text": text,
|
| 672 |
"screenshot": screenshot_b64,
|
| 673 |
"url": driver.current_url,
|
| 674 |
"title": driver.title
|
|
|
|
| 677 |
logger.error(f"[INTERACT-TYPE] Erro: {e}")
|
| 678 |
raise HTTPException(status_code=500, detail=str(e))
|
| 679 |
|
| 680 |
+
|
| 681 |
@app.post("/interact/key")
|
| 682 |
async def interact_key(request: InteractKeyRequest, auth=Depends(verify_token)):
|
| 683 |
+
"""Pressiona tecla especial usando ActionChains"""
|
| 684 |
session = session_manager.get_session(request.session_id)
|
| 685 |
driver = session['driver']
|
| 686 |
|
|
|
|
| 703 |
raise HTTPException(status_code=400, detail=f"Tecla não suportada: {request.key}")
|
| 704 |
|
| 705 |
actions = ActionChains(driver)
|
| 706 |
+
actions.send_keys(key)
|
| 707 |
+
actions.perform()
|
| 708 |
|
| 709 |
+
time.sleep(0.8)
|
| 710 |
|
| 711 |
screenshot = driver.get_screenshot_as_png()
|
| 712 |
screenshot_b64 = base64.b64encode(screenshot).decode('utf-8')
|
|
|
|
| 724 |
logger.error(f"[INTERACT-KEY] Erro: {e}")
|
| 725 |
raise HTTPException(status_code=500, detail=str(e))
|
| 726 |
|
| 727 |
+
|
| 728 |
@app.post("/interact/scroll")
|
| 729 |
async def interact_scroll(request: SessionRequest, auth=Depends(verify_token)):
|
| 730 |
"""Rola a página para baixo"""
|
|
|
|
| 748 |
logger.error(f"[INTERACT-SCROLL] Erro: {e}")
|
| 749 |
raise HTTPException(status_code=500, detail=str(e))
|
| 750 |
|
| 751 |
+
|
| 752 |
@app.post("/interact/clear-and-type")
|
| 753 |
async def interact_clear_and_type(request: InteractTypeRequest, auth=Depends(verify_token)):
|
| 754 |
"""Limpa o campo focado e digita novo texto"""
|
|
|
|
| 756 |
driver = session['driver']
|
| 757 |
|
| 758 |
try:
|
| 759 |
+
# Limpar com Ctrl+A + Delete
|
| 760 |
+
actions = ActionChains(driver)
|
| 761 |
+
actions.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL)
|
| 762 |
+
actions.send_keys(Keys.DELETE)
|
| 763 |
+
actions.perform()
|
| 764 |
+
|
| 765 |
+
time.sleep(0.2)
|
| 766 |
+
|
| 767 |
+
# Digitar novo texto
|
| 768 |
+
actions = ActionChains(driver)
|
| 769 |
+
actions.send_keys(request.text)
|
| 770 |
+
actions.perform()
|
| 771 |
|
| 772 |
time.sleep(0.3)
|
| 773 |
|
|
|
|
| 786 |
raise HTTPException(status_code=500, detail=str(e))
|
| 787 |
|
| 788 |
|
| 789 |
+
|
| 790 |
# ============================================================
|
| 791 |
# ROTAS DE BUSCA NO SITE
|
| 792 |
# ============================================================
|