#!/root/venvs/itb/bin/python import sys import os import time import argparse from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.actions.action_builder import ActionBuilder from selenium.webdriver.common.actions.mouse_button import MouseButton from selenium.webdriver.common.by import By class ClickExecutor: def __init__(self, debug_port: int, verbose: bool = False): """Initialize connection to Chrome instance""" self.verbose = verbose options = webdriver.ChromeOptions() options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debug_port}") self.driver = webdriver.Chrome(options=options) # Load JavaScript files js_dir = os.path.join(os.path.dirname(__file__), "..", "js") self.js_code = "" for filename in ["viewport.js", "mouse.js"]: with open(os.path.join(js_dir, filename)) as f: self.js_code += f.read() + "\n" # Get window and viewport info self.window_size = self.driver.get_window_size() self.viewport_size = self.get_viewport_size() self.scroll_position = self.get_scroll_position() def log(self, msg: str): """Log message if verbose mode is enabled""" if self.verbose: print(f"DEBUG: {msg}", file=sys.stderr) def get_viewport_size(self): """Get viewport dimensions""" return self.driver.execute_script(self.js_code + "; return getViewportInfo();") def get_scroll_position(self): """Get current scroll position""" return self.driver.execute_script(self.js_code + "; return getViewportInfo();") def get_mouse_position(self): """Get current mouse coordinates""" return self.driver.execute_script(self.js_code + "; return getMousePosition();") def scroll_to_target(self, x: int, y: int): """Scroll to make target coordinates visible""" self.driver.execute_script( "window.scrollTo(arguments[0], arguments[1]);", max(0, x - self.viewport_size['width'] // 2), max(0, y - self.viewport_size['height'] // 2) ) time.sleep(0.5) self.scroll_position = self.get_scroll_position() def get_element_at_position(self, x: int, y: int): """Get the element at the specified coordinates""" return self.driver.execute_script( self.js_code + "; return getElementAtPosition(arguments[0], arguments[1]);", x, y ) def execute_click(self, x: int, y: int, double: bool = False, right: bool = False, element_id: str = None): """Execute click operation with improved handling""" try: self.log(f"Attempting {'double' if double else 'right' if right else 'single'} click at ({x}, {y})") if element_id: # Try to find element by ID first try: element = self.driver.find_element(By.ID, element_id) self.log(f"Found element by ID: {element_id}") except: self.log(f"Failed to find element by ID: {element_id}") return False else: # Scroll to make target visible self.scroll_to_target(x, y) # Get element at position element = self.get_element_at_position(x, y) if not element: self.log(f"No element found at coordinates ({x}, {y})") return False # Try clicking the element try: actions = ActionChains(self.driver) if element_id: actions.move_to_element(element) else: actions.move_by_offset(x, y) if right: actions.context_click() elif double: actions.double_click() else: actions.click() actions.perform() return True except Exception as e: self.log(f"Direct click failed: {str(e)}") self.log("Attempting JavaScript click fallback") try: self.driver.execute_script("arguments[0].click();", element) return True except Exception as js_error: self.log(f"JavaScript click failed: {str(js_error)}") return False except Exception as e: self.log(f"Click operation failed: {str(e)}") return False def main(): parser = argparse.ArgumentParser(description='Execute mouse clicks in Chrome') parser.add_argument('debug_port', type=int, help='Chrome debug port') parser.add_argument('-x', type=int, help='X coordinate') parser.add_argument('-y', type=int, help='Y coordinate') parser.add_argument('--double', action='store_true', help='Perform double click') parser.add_argument('--right', action='store_true', help='Perform right click') parser.add_argument('--id', help='Element ID to click') parser.add_argument('-v', '--verbose', action='store_true', help='Enable debug output') args = parser.parse_args() if args.double and args.right: print("Cannot specify both --double and --right", file=sys.stderr) sys.exit(1) if not args.id and (args.x is None or args.y is None): print("Must specify either element ID or both x and y coordinates", file=sys.stderr) sys.exit(1) try: executor = ClickExecutor(args.debug_port, args.verbose) success = executor.execute_click( x=args.x if args.x is not None else 0, y=args.y if args.y is not None else 0, double=args.double, right=args.right, element_id=args.id ) sys.exit(0 if success else 1) except Exception as e: if args.verbose: print(f"Fatal error: {str(e)}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()