| import pyautogui | |
| from dataclasses import dataclass | |
| from typing import Dict | |
| from .config import CONFIG | |
| class KalmanFilter1D: | |
| """Simple 1D Kalman Filter for linear position and velocity estimation.""" | |
| def __init__(self, q=0.01, r=0.1): | |
| self.q = q # process noise covariance | |
| self.r = r # measurement noise covariance | |
| self.x = 0.0 # estimate | |
| self.p = 1.0 # estimation error covariance | |
| def update(self, measurement: float) -> float: | |
| # Prediction update | |
| self.p = self.p + self.q | |
| # Measurement update | |
| k = self.p / (self.p + self.r) | |
| self.x = self.x + k * (measurement - self.x) | |
| self.p = (1 - k) * self.p | |
| return self.x | |
| class TremorFilter: | |
| """Low-pass moving average filter to remove micro-shakes and tremors (>6 Hz).""" | |
| def __init__(self, window_size: int = 3): | |
| self.window_size = window_size | |
| self.history_x = [] | |
| self.history_y = [] | |
| def filter(self, dx: float, dy: float) -> tuple[float, float]: | |
| self.history_x.append(dx) | |
| self.history_y.append(dy) | |
| if len(self.history_x) > self.window_size: | |
| self.history_x.pop(0) | |
| self.history_y.pop(0) | |
| avg_x = sum(self.history_x) / len(self.history_x) | |
| avg_y = sum(self.history_y) / len(self.history_y) | |
| return avg_x, avg_y | |
| class MouseController: | |
| sensitivity: float = CONFIG.get("sensitivity", 0.5) | |
| click_count: int = 0 | |
| double_click_count: int = 0 | |
| right_click_count: int = 0 | |
| scroll_count: int = 0 | |
| def __post_init__(self): | |
| pyautogui.FAILSAFE = True | |
| pyautogui.PAUSE = 0 | |
| self.kalman_x = KalmanFilter1D(q=0.02, r=0.08) | |
| self.kalman_y = KalmanFilter1D(q=0.02, r=0.08) | |
| self.tremor_filter = TremorFilter(window_size=3) | |
| def move(self, dx: float, dy: float) -> None: | |
| dx = max(-50.0, min(50.0, dx * self.sensitivity)) | |
| dy = max(-50.0, min(50.0, dy * self.sensitivity)) | |
| # Apply tremor moving average filter | |
| dx, dy = self.tremor_filter.filter(dx, dy) | |
| # Apply 1D Kalman filter to smooth trajectories | |
| dx = self.kalman_x.update(dx) | |
| dy = self.kalman_y.update(dy) | |
| if abs(dx) < 0.15 and abs(dy) < 0.15: | |
| return | |
| pyautogui.moveRel(dx, dy, duration=0.0) | |
| def click(self, button: str = 'left') -> None: | |
| pyautogui.click(button=button) | |
| if button == 'left': | |
| self.click_count += 1 | |
| elif button == 'right': | |
| self.right_click_count += 1 | |
| def double_click(self) -> None: | |
| pyautogui.doubleClick() | |
| self.double_click_count += 1 | |
| def scroll(self, delta: int) -> None: | |
| pyautogui.scroll(delta) | |
| self.scroll_count += 1 | |
| def get_stats(self) -> Dict: | |
| return { | |
| "clicks": self.click_count, | |
| "double_clicks": self.double_click_count, | |
| "right_clicks": self.right_click_count, | |
| "scrolls": self.scroll_count | |
| } |
Xet Storage Details
- Size:
- 3.02 kB
- Xet hash:
- c5a05ef7bb251d5b78660c54d0f80c746ea1cc5a9c5a526d5d9b676baf0bea24
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.