lots done

This commit is contained in:
Boki 2026-03-02 11:17:37 -05:00
parent 1ba7c39c30
commit fbd0ba445a
59 changed files with 6074 additions and 3598 deletions

View file

@ -0,0 +1,99 @@
using Roboto.Core;
namespace Roboto.Input;
public sealed class Humanizer
{
private readonly BotConfig _config;
private readonly Random _rng = new();
private readonly Queue<long> _actionTimestamps = new();
private bool _hasSpare;
private double _spare;
public Humanizer(BotConfig config)
{
_config = config;
}
/// <summary>
/// Returns baseMs ± gaussian noise, clamped to [MinReaction, MaxReaction].
/// </summary>
public int GaussianDelay(int baseMs)
{
var noise = NextGaussian() * _config.TimingNoiseStdDev * baseMs;
var result = (int)(baseMs + noise);
return Math.Clamp(result, _config.MinReactionDelayMs, _config.MaxReactionDelayMs);
}
/// <summary>
/// Adds gaussian pixel offset within configured jitter radius.
/// </summary>
public (int x, int y) JitterPosition(int x, int y)
{
var r = _config.ClickJitterRadius;
if (r <= 0) return (x, y);
var dx = (int)(NextGaussian() * r * 0.5);
var dy = (int)(NextGaussian() * r * 0.5);
return (x + dx, y + dy);
}
/// <summary>
/// Returns true if we're over the APM cap and should throttle.
/// </summary>
public bool ShouldThrottle()
{
var now = Environment.TickCount64;
// Purge timestamps older than 60s
while (_actionTimestamps.Count > 0 && now - _actionTimestamps.Peek() > 60_000)
_actionTimestamps.Dequeue();
return _actionTimestamps.Count >= _config.MaxApm;
}
/// <summary>
/// Records an action for APM tracking.
/// </summary>
public void RecordAction()
{
_actionTimestamps.Enqueue(Environment.TickCount64);
}
/// <summary>
/// Returns base interval ± jitter% for poll/tick randomization.
/// </summary>
public double RandomizedInterval(double baseMs)
{
var jitter = _config.PollIntervalJitter;
if (jitter <= 0) return baseMs;
var factor = 1.0 + (NextGaussian() * jitter * 0.5);
return baseMs * Math.Clamp(factor, 1.0 - jitter, 1.0 + jitter);
}
/// <summary>
/// Box-Muller transform for gaussian random numbers.
/// </summary>
private double NextGaussian()
{
if (_hasSpare)
{
_hasSpare = false;
return _spare;
}
double u, v, s;
do
{
u = _rng.NextDouble() * 2.0 - 1.0;
v = _rng.NextDouble() * 2.0 - 1.0;
s = u * u + v * v;
} while (s >= 1.0 || s == 0.0);
var mul = Math.Sqrt(-2.0 * Math.Log(s) / s);
_spare = v * mul;
_hasSpare = true;
return u * mul;
}
}

View file

@ -0,0 +1,139 @@
using InputInterceptorNS;
using Roboto.Core;
using Serilog;
namespace Roboto.Input;
public sealed class InterceptionInputController : IInputController, IDisposable
{
private readonly Humanizer? _humanizer;
private KeyboardHook? _keyboard;
private MouseHook? _mouse;
private bool _disposed;
public bool IsInitialized => _keyboard is not null && _mouse is not null;
public InterceptionInputController(Humanizer? humanizer = null)
{
_humanizer = humanizer;
}
public bool Initialize()
{
try
{
if (!InputInterceptor.CheckDriverInstalled())
{
Log.Warning("Interception driver not installed");
return false;
}
if (!InputInterceptor.Initialize())
{
Log.Warning("Failed to load Interception native library");
return false;
}
_keyboard = new KeyboardHook();
_mouse = new MouseHook();
Log.Information("Interception input controller initialized");
return true;
}
catch (Exception ex)
{
Log.Error(ex, "Failed to initialize Interception hooks");
_keyboard = null;
_mouse = null;
return false;
}
}
public void KeyDown(ushort scanCode)
{
_keyboard?.SimulateKeyDown((KeyCode)scanCode);
}
public void KeyUp(ushort scanCode)
{
_keyboard?.SimulateKeyUp((KeyCode)scanCode);
}
public void KeyPress(ushort scanCode, int holdMs = 50)
{
if (_humanizer is not null)
{
if (_humanizer.ShouldThrottle()) return;
holdMs = _humanizer.GaussianDelay(holdMs);
_humanizer.RecordAction();
}
_keyboard?.SimulateKeyPress((KeyCode)scanCode, holdMs);
}
public void MouseMoveTo(int x, int y)
{
_mouse?.SetCursorPosition(x, y, false);
}
public void MouseMoveBy(int dx, int dy)
{
_mouse?.MoveCursorBy(dx, dy, false);
}
public void LeftClick(int x, int y)
{
if (_humanizer is not null)
{
if (_humanizer.ShouldThrottle()) return;
(x, y) = _humanizer.JitterPosition(x, y);
Thread.Sleep(_humanizer.GaussianDelay(10));
_humanizer.RecordAction();
}
MouseMoveTo(x, y);
Thread.Sleep(_humanizer is not null ? _humanizer.GaussianDelay(10) : 10);
_mouse?.SimulateLeftButtonClick(_humanizer?.GaussianDelay(50) ?? 50);
}
public void RightClick(int x, int y)
{
if (_humanizer is not null)
{
if (_humanizer.ShouldThrottle()) return;
(x, y) = _humanizer.JitterPosition(x, y);
Thread.Sleep(_humanizer.GaussianDelay(10));
_humanizer.RecordAction();
}
MouseMoveTo(x, y);
Thread.Sleep(_humanizer is not null ? _humanizer.GaussianDelay(10) : 10);
_mouse?.SimulateRightButtonClick(_humanizer?.GaussianDelay(50) ?? 50);
}
public void LeftDown()
{
_mouse?.SimulateLeftButtonDown();
}
public void LeftUp()
{
_mouse?.SimulateLeftButtonUp();
}
public void RightDown()
{
_mouse?.SimulateRightButtonDown();
}
public void RightUp()
{
_mouse?.SimulateRightButtonUp();
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_keyboard?.Dispose();
_mouse?.Dispose();
_keyboard = null;
_mouse = null;
}
}

View file

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="InputInterceptor" Version="2.2.1" />
<PackageReference Include="Serilog" Version="4.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Roboto.Core\Roboto.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="interception.dll" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,56 @@
namespace Roboto.Input;
/// <summary>
/// Hardware scan codes for keyboard input via Interception driver.
/// </summary>
public static class ScanCodes
{
// WASD movement
public const ushort W = 0x11;
public const ushort A = 0x1E;
public const ushort S = 0x1F;
public const ushort D = 0x20;
// Number row
public const ushort Key1 = 0x02;
public const ushort Key2 = 0x03;
public const ushort Key3 = 0x04;
public const ushort Key4 = 0x05;
public const ushort Key5 = 0x06;
public const ushort Key6 = 0x07;
public const ushort Key7 = 0x08;
public const ushort Key8 = 0x09;
public const ushort Key9 = 0x0A;
public const ushort Key0 = 0x0B;
// Modifiers
public const ushort LShift = 0x2A;
public const ushort RShift = 0x36;
public const ushort LCtrl = 0x1D;
public const ushort LAlt = 0x38;
// Common keys
public const ushort Escape = 0x01;
public const ushort Tab = 0x0F;
public const ushort Space = 0x39;
public const ushort Enter = 0x1C;
public const ushort Backspace = 0x0E;
// Function keys
public const ushort F1 = 0x3B;
public const ushort F2 = 0x3C;
public const ushort F3 = 0x3D;
public const ushort F4 = 0x3E;
public const ushort F5 = 0x3F;
// Letters (commonly used)
public const ushort Q = 0x10;
public const ushort E = 0x12;
public const ushort R = 0x13;
public const ushort T = 0x14;
public const ushort I = 0x17;
public const ushort F = 0x21;
// Slash (for chat commands like /hideout)
public const ushort Slash = 0x35;
}

Binary file not shown.