#![allow(dead_code, unused_imports, unused_variables)]
pub mod keyboard;
pub mod mouse;
pub mod screen;
pub mod window;
pub use keyboard::KeyboardController;
pub use mouse::MouseController;
pub use screen::ScreenCapture;
pub use window::WindowManager;
use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Instant;
use tracing::warn;
#[derive(Debug)]
pub struct ActionRateLimiter {
max_actions_per_sec: u32,
last_action_ms: AtomicU64,
actions_in_window: AtomicU64,
}
impl ActionRateLimiter {
pub fn new(max_actions_per_sec: u32) -> Self {
Self {
max_actions_per_sec,
last_action_ms: AtomicU64::new(0),
actions_in_window: AtomicU64::new(0),
}
}
pub fn check(&self) -> bool {
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let last = self.last_action_ms.load(Ordering::Relaxed);
let window_ms = 1000;
if now_ms - last > window_ms {
self.last_action_ms.store(now_ms, Ordering::Relaxed);
self.actions_in_window.store(1, Ordering::Relaxed);
true
} else {
let count = self.actions_in_window.fetch_add(1, Ordering::Relaxed) + 1;
count <= self.max_actions_per_sec as u64
}
}
}
impl Default for ActionRateLimiter {
fn default() -> Self {
Self::new(10) }
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MovementProfile {
#[default]
Linear,
EaseInOut,
Bezier,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TypingProfile {
#[serde(default)]
pub base_delay_ms: u64,
#[serde(default)]
pub variation_ms: u64,
}
const BLOCKED_COMBOS: &[&str] = &[
"ctrl+alt+delete",
"cmd+q", "alt+f4", "ctrl+alt+f1", "ctrl+alt+f2",
"ctrl+alt+f3",
];
pub fn is_blocked_combo(combo: &str) -> bool {
let normalized = combo.to_lowercase().replace(' ', "");
BLOCKED_COMBOS
.iter()
.any(|blocked| normalized == blocked.replace(' ', ""))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rate_limiter_allows_first_action() {
let limiter = ActionRateLimiter::new(5);
assert!(limiter.check());
}
#[test]
fn test_blocked_combos() {
assert!(is_blocked_combo("ctrl+alt+delete"));
assert!(is_blocked_combo("Ctrl+Alt+Delete"));
assert!(is_blocked_combo("cmd+q"));
assert!(!is_blocked_combo("ctrl+c"));
assert!(!is_blocked_combo("ctrl+v"));
assert!(!is_blocked_combo("ctrl+s"));
}
#[test]
fn test_movement_profile_default() {
let profile = MovementProfile::default();
assert!(matches!(profile, MovementProfile::Linear));
}
#[test]
fn test_typing_profile_default() {
let profile = TypingProfile::default();
assert_eq!(profile.base_delay_ms, 0);
}
}