lots done
This commit is contained in:
parent
1ba7c39c30
commit
fbd0ba445a
59 changed files with 6074 additions and 3598 deletions
99
src/Roboto.Input/Humanizer.cs
Normal file
99
src/Roboto.Input/Humanizer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
139
src/Roboto.Input/InterceptionInputController.cs
Normal file
139
src/Roboto.Input/InterceptionInputController.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
17
src/Roboto.Input/Roboto.Input.csproj
Normal file
17
src/Roboto.Input/Roboto.Input.csproj
Normal 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>
|
||||
56
src/Roboto.Input/ScanCodes.cs
Normal file
56
src/Roboto.Input/ScanCodes.cs
Normal 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;
|
||||
}
|
||||
BIN
src/Roboto.Input/interception.dll
Normal file
BIN
src/Roboto.Input/interception.dll
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue