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:
2025-05-25 13:49:08 +02:00
parent a62778a030
commit cf6a768f6b
18 changed files with 880 additions and 107 deletions
+116
View File
@@ -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}"')