"""Mueve el ratón aleatoriamente solo tras un periodo de inactividad del usuario (solo Windows).""" import ctypes import ctypes.wintypes import random import time import pyautogui # Segundos sin uso de teclado/ratón antes de empezar a mover el cursor TIEMPO_INACTIVIDAD = 180 # Desplazamiento máximo (píxeles) en cada eje DESPLAZAMIENTO_MAX = 50 # Intervalo de comprobación de actividad (segundos) INTERVALO_COMPROBACION = 0.2 class LASTINPUTINFO(ctypes.Structure): """Estructura de Win32 que recibe el instante de la última entrada del usuario.""" _fields_ = [("cbSize", ctypes.wintypes.UINT), ("dwTime", ctypes.wintypes.DWORD)] def obtener_ultima_entrada(): """ Obtiene el instante (en ticks del sistema, ms) de la última entrada de teclado o ratón. :return: valor dwTime devuelto por GetLastInputInfo. """ info = LASTINPUTINFO() info.cbSize = ctypes.sizeof(LASTINPUTINFO) if not ctypes.windll.user32.GetLastInputInfo(ctypes.byref(info)): raise ctypes.WinError() return info.dwTime def segundos_inactivo(): """ Calcula los segundos transcurridos desde la última entrada de teclado o ratón. :return: segundos de inactividad. """ ahora = ctypes.windll.kernel32.GetTickCount() & 0xFFFFFFFF # Máscara para tolerar el desbordamiento de GetTickCount (~49,7 días) return ((ahora - obtener_ultima_entrada()) & 0xFFFFFFFF) / 1000.0 def esperar_inactividad(): """Bloquea hasta que el usuario lleve TIEMPO_INACTIVIDAD segundos sin usar teclado ni ratón.""" while segundos_inactivo() < TIEMPO_INACTIVIDAD: time.sleep(1) def mover_mientras_inactivo(): """ Mueve el ratón aleatoriamente hasta detectar actividad real del usuario. Los movimientos propios también actualizan GetLastInputInfo, por lo que tras cada movimiento se guarda el dwTime resultante; cualquier cambio posterior indica uso real. """ while True: pyautogui.moveRel(random.randrange(-DESPLAZAMIENTO_MAX, DESPLAZAMIENTO_MAX), random.randrange(-DESPLAZAMIENTO_MAX, DESPLAZAMIENTO_MAX)) ultima_propia = obtener_ultima_entrada() fin_pausa = time.monotonic() + random.randrange(2, 5) while time.monotonic() < fin_pausa: time.sleep(INTERVALO_COMPROBACION) if obtener_ultima_entrada() != ultima_propia: return def main(): """Bucle principal: espera la inactividad, mueve el ratón y se detiene al detectar uso.""" while True: try: esperar_inactividad() mover_mientras_inactivo() except Exception: time.sleep(1) if __name__ == "__main__": main()