- Mover.ps1: activación tras inactividad, parada al detectar uso, deslizamiento animado visible, mensajes de estado y cierre de instancias previas. - main.py: movimiento solo tras 3 minutos de inactividad. - Documentación: README, AGENTS, CHANGELOG y TODO. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
"""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()
|