Refactor and enhance MCP client and server functionality
- Removed prueba_cliente_mcp.py as it was no longer needed. - Updated prueba_loop_agente.py to integrate MCPServerRunner for managing server instances. - Modified prueba_mcp.py to implement a new structure for starting and stopping MCP servers. - Enhanced AgenteAI class to support multiple MCP blocks execution. - Improved MCPClient with timeout handling for tool calls. - Added new sandbox files for children's stories. - Created a simple ERP system with a main entry point. - Added unit tests for the ERP system. - Implemented MCPServerRunner to manage server processes. - Developed server_files.py to handle file operations securely within a sandbox environment. - Introduced ElementoWeb and Navegador classes for web scraping functionalities. - Enhanced Scrapper and Tab classes for better interaction with web pages.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
import random
|
||||
import asyncio
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.ScrappingWeb.Tab import Tab
|
||||
|
||||
class ElementoWeb:
|
||||
def __init__(self, tab: "Tab", object_id: str):
|
||||
self.tab = tab
|
||||
self.object_id = object_id
|
||||
|
||||
async def scroll_into_view(self):
|
||||
try:
|
||||
await self.tab._enviar("Runtime.callFunctionOn", {
|
||||
"objectId": self.object_id,
|
||||
"functionDeclaration": "function() { this.scrollIntoView({block: 'center'}); }",
|
||||
"awaitPromise": True
|
||||
})
|
||||
print("📜 Elemento desplazado a la vista.")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error al hacer scroll hacia el elemento: {e}")
|
||||
|
||||
@classmethod
|
||||
def from_node(cls, tab: "Tab", node_id: int) -> "ElementoWeb":
|
||||
# Creamos un objectId a partir del nodeId usando DOM.resolveNode
|
||||
cls._node_id = node_id
|
||||
cls._resolved_object_id = None # Lazy resolution opcional
|
||||
return cls(tab, object_id=None)
|
||||
|
||||
async def click(self):
|
||||
try:
|
||||
await self.scroll_into_view()
|
||||
|
||||
# Resolver objectId si es necesario
|
||||
if not self.object_id and hasattr(self, "_node_id"):
|
||||
resolved = await self.tab._enviar("DOM.resolveNode", {"nodeId": self._node_id})
|
||||
self.object_id = resolved["object"]["objectId"]
|
||||
|
||||
if not self.object_id:
|
||||
raise ValueError("No se puede obtener objectId del elemento para hacer click.")
|
||||
|
||||
# Obtener nodeId
|
||||
node_result = await self.tab._enviar("DOM.describeNode", {
|
||||
"objectId": self.object_id
|
||||
})
|
||||
|
||||
node_id = node_result["node"]["nodeId"]
|
||||
|
||||
# Obtener coordenadas con fallback
|
||||
try:
|
||||
box_model = await self.tab._enviar("DOM.getBoxModel", {"nodeId": node_id})
|
||||
content = box_model["model"]["content"]
|
||||
x = (content[0] + content[2]) / 2
|
||||
y = (content[1] + content[5]) / 2
|
||||
except:
|
||||
quads_result = await self.tab._enviar("DOM.getContentQuads", {"nodeId": node_id})
|
||||
quad = quads_result["quads"][0]
|
||||
x = (quad[0] + quad[4]) / 2
|
||||
y = (quad[1] + quad[5]) / 2
|
||||
|
||||
# Simular movimiento humano del mouse
|
||||
start_x, start_y = x + random.uniform(-100, 100), y + random.uniform(-100, 100)
|
||||
steps = random.randint(5, 12)
|
||||
for i in range(1, steps + 1):
|
||||
curr_x = start_x + (x - start_x) * i / steps + random.uniform(-1, 1)
|
||||
curr_y = start_y + (y - start_y) * i / steps + random.uniform(-1, 1)
|
||||
await self.tab._enviar("Input.dispatchMouseEvent", {
|
||||
"type": "mouseMoved",
|
||||
"x": curr_x,
|
||||
"y": curr_y,
|
||||
})
|
||||
await asyncio.sleep(random.uniform(0.01, 0.05))
|
||||
|
||||
# Click humano
|
||||
await self.tab._enviar("Input.dispatchMouseEvent", {
|
||||
"type": "mousePressed",
|
||||
"x": x,
|
||||
"y": y,
|
||||
"button": "left",
|
||||
"clickCount": 1
|
||||
})
|
||||
await asyncio.sleep(random.uniform(0.05, 0.15))
|
||||
await self.tab._enviar("Input.dispatchMouseEvent", {
|
||||
"type": "mouseReleased",
|
||||
"x": x,
|
||||
"y": y,
|
||||
"button": "left",
|
||||
"clickCount": 1
|
||||
})
|
||||
|
||||
print(f"🖱️ Click humano simulado en ({x:.1f}, {y:.1f})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error al hacer click físico: {e}")
|
||||
print("🧪 Intentando fallback con JavaScript click()...")
|
||||
await self.click_js()
|
||||
|
||||
|
||||
async def click_js(self):
|
||||
try:
|
||||
await self.tab._enviar("Runtime.callFunctionOn", {
|
||||
"objectId": self.object_id,
|
||||
"functionDeclaration": "function() { this.click(); }",
|
||||
"awaitPromise": True
|
||||
})
|
||||
print("🖱️ Click simulado por JavaScript (element.click())")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error al ejecutar click en JS: {e}")
|
||||
|
||||
async def obtener_texto(self) -> Optional[str]:
|
||||
return await self.tab.evaluar_js(f'document.getElementById("{self.object_id}").textContent')
|
||||
|
||||
async def escribir_texto(self, texto: str):
|
||||
await self.tab.evaluar_js(f'document.getElementById("{self.object_id}").value = "{texto}"')
|
||||
Reference in New Issue
Block a user