wabisabio 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wabisabio-0.1.0/LICENSE +21 -0
- wabisabio-0.1.0/PKG-INFO +424 -0
- wabisabio-0.1.0/README.md +387 -0
- wabisabio-0.1.0/pyproject.toml +29 -0
- wabisabio-0.1.0/setup.cfg +4 -0
- wabisabio-0.1.0/wabisabio.egg-info/PKG-INFO +424 -0
- wabisabio-0.1.0/wabisabio.egg-info/SOURCES.txt +9 -0
- wabisabio-0.1.0/wabisabio.egg-info/dependency_links.txt +1 -0
- wabisabio-0.1.0/wabisabio.egg-info/requires.txt +2 -0
- wabisabio-0.1.0/wabisabio.egg-info/top_level.txt +1 -0
- wabisabio-0.1.0/wabisabio.py +583 -0
wabisabio-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sam Howle
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
wabisabio-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wabisabio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Human-like input automation framework for Windows
|
|
5
|
+
Author-email: Sam Howle <samhowle@protonmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Sam Howle
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Repository, https://github.com/sam-howle/WabiSabIO
|
|
28
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
31
|
+
Requires-Python: >=3.9
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Requires-Dist: numpy>=1.20
|
|
35
|
+
Requires-Dist: scanput
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# WabiSabIO
|
|
39
|
+
"Perfect" your input automation through injected imperfections
|
|
40
|
+
|
|
41
|
+
`wabisabio` is an input automation Python library for Windows keyboard and mouse inputs with the specific goal of making inputs appear more human-like. The framework features center-biased coordinate and timing randomization, curved mouse movement, destination overshoot and correction, and idle mouse jitter to model the small imperfections that naturally emerge during human interaction.
|
|
42
|
+
|
|
43
|
+
<img src="demo.gif" width="320">
|
|
44
|
+
<sub><i>*Fire hydrant image recognition module sold separately.</i></sub>
|
|
45
|
+
|
|
46
|
+
## Introduction
|
|
47
|
+
|
|
48
|
+
Most keyboard and mouse automation libraries optimize for one thing: reliably interacting with a user interface. The resulting inputs are typically fast, precise, and perfectly repeatable, making them easy to distinguish from those of a real user through even relatively simple behavioral analysis.
|
|
49
|
+
|
|
50
|
+
A common response is to introduce randomness by varying delays, mouse speed, cursor landing location, or path generation. While this reduces consistency, it often produces its own unrealistic behavior. Human input is not uniformly random. Users tend to aim near the center of targets, maintain relatively consistent movement characteristics, occasionally overshoot a destination, and naturally alternate between periods of activity and inactivity.
|
|
51
|
+
|
|
52
|
+
This observation led to an interesting question:
|
|
53
|
+
|
|
54
|
+
> **How much more human can synthetic input appear using nothing more than a handful of statistical distributions and simple geometric techniques?**
|
|
55
|
+
|
|
56
|
+
`wabisabio` is an attempt to answer that question.
|
|
57
|
+
|
|
58
|
+
Rather than generating deterministic input and injecting randomness afterward, the library models many of the small imperfections that naturally emerge during human interaction. Mouse movement follows Bézier curves with hand tremor, destination overshoot and correction, timing delays are sampled from configurable statistical distributions, and higher-level primitives provide composable building blocks for constructing more natural interaction patterns while remaining lightweight and easy to understand.
|
|
59
|
+
|
|
60
|
+
### Design Philosophy
|
|
61
|
+
|
|
62
|
+
Humans are consistent in their inconsistency.
|
|
63
|
+
|
|
64
|
+
When using a UI, people tend to aim near the center of targets, follow recognizable movement patterns, and occasionally overshoot. While the specifics vary person to person, the tendencies do not.
|
|
65
|
+
|
|
66
|
+
A simple example: if a valid click region spans 100×100 pixels, a basic script might pick a random (x, y) uniformly from that space. Technically random, but the implication is that users click the corners just as often as the center. They don't. People aim for the middle of a target and drift from it with decreasing probability the further out you go. It's center-biased, not flat.
|
|
67
|
+
|
|
68
|
+
`wabisabio` treats randomness the same way. Instead of uniform noise, its primitives sample from configurable distributions: shape, spread, and center are all adjustable by the caller.
|
|
69
|
+
|
|
70
|
+
The goal is behavior that looks like a person with habits rather than a script rolling dice.
|
|
71
|
+
|
|
72
|
+
The heatmaps shown below were generated using 10,000 sampled coordinates from each distribution:
|
|
73
|
+
|
|
74
|
+
<figure>
|
|
75
|
+
<img src="DeathBy10000Clicks.png">
|
|
76
|
+
<figcaption>
|
|
77
|
+
<i>Uniform sampling (left), center-biased sampling (center), customized center-biased sampling (right)</i>
|
|
78
|
+
</figcaption>
|
|
79
|
+
</figure>
|
|
80
|
+
|
|
81
|
+
The following examples generate the three distributions shown above:
|
|
82
|
+
```python
|
|
83
|
+
# Uniform sampling (every valid coordinate is equally likely)
|
|
84
|
+
random_x = random.randint(center_x - radius_x, center_x + radius_x)
|
|
85
|
+
random_y = random.randint(center_y - radius_y, center_y + radius_y)
|
|
86
|
+
|
|
87
|
+
# Center-biased sampling (default)
|
|
88
|
+
random_x, random_y = wabisabio.randomize_coordinate_within_range(
|
|
89
|
+
center_x,
|
|
90
|
+
center_y,
|
|
91
|
+
radius_x=radius_x,
|
|
92
|
+
radius_y=radius_y,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Customized distribution
|
|
96
|
+
random_x, random_y = wabisabio.randomize_coordinate_within_range(
|
|
97
|
+
center_x,
|
|
98
|
+
center_y,
|
|
99
|
+
radius_x=radius_x,
|
|
100
|
+
radius_y=radius_y,
|
|
101
|
+
sigmas_to_edge_x=3.6, # Narrower horizontal spread
|
|
102
|
+
sigmas_to_edge_y=3.6, # Narrower vertical spread
|
|
103
|
+
bias_x=-0.3, # Left-biased target selection
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
While pixel landing coordinate randomization is one of the easiest behaviors to visualize, the same statistical distributions are used throughout the library and are applied to:
|
|
108
|
+
|
|
109
|
+
* Micro-delays between actions (e.g., moving a mouse before clicking)
|
|
110
|
+
* Action timing delays
|
|
111
|
+
* Mouse click and keystroke hold durations
|
|
112
|
+
* Mouse movement speed (relative to travel distance)
|
|
113
|
+
* Mouse movement curvature
|
|
114
|
+
* Primitive functions for center-biased randomization
|
|
115
|
+
|
|
116
|
+
Together, these primitives provide lightweight, composable building blocks for constructing more natural interaction patterns.
|
|
117
|
+
|
|
118
|
+
## Installation
|
|
119
|
+
`wabisabio` requiures Python 3.9 or higher.
|
|
120
|
+
```
|
|
121
|
+
pip install wabisabio
|
|
122
|
+
```
|
|
123
|
+
Alternatively, clone this repo and build it locally:
|
|
124
|
+
```
|
|
125
|
+
git clone https://github.com/sam-howle/WabiSabIO.git
|
|
126
|
+
cd wabisabio
|
|
127
|
+
pip install .
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Usage
|
|
131
|
+
|
|
132
|
+
The following table provides a brief overview of the functions exposed by `wabisabio`:
|
|
133
|
+
|
|
134
|
+
| Function | Description |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `move_mouse(dest_x, dest_y)` | Move mouse from current position to the supplied `(x, y)` screen coordinates taking a curved path |
|
|
137
|
+
| `press_key(key)` | Presses the supplied `key` and releases after a short, randomized delay |
|
|
138
|
+
| `left_click()` | Performs a left click and releases after a short, randomized delay |
|
|
139
|
+
| `right_click()` | Performs a right click and releases after a short, randomized delay |
|
|
140
|
+
| `lagged_press_key(key)` | Same as `press_key()`, but with randomized delays before and/or after the event |
|
|
141
|
+
| `lagged_left_click()` | Same as `left_click()`, but with randomized delays before and/or after the event |
|
|
142
|
+
| `lagged_right_click()` | Same as `right_click()`, but with randomized delays before and/or after the event |
|
|
143
|
+
| `modifier_key_press(modifier, key)` | Presses one or more modifier keys (e.g. `"shift"`, `"ctrl"`), then presses `key`, then releases all in reverse order with randomized delays between each event |
|
|
144
|
+
| `modifier_left_click(modifier)` | Same as `left_click()`, but holds one or more modifier keys for the duration of the click |
|
|
145
|
+
| `modifier_right_click(modifier)` | Same as `right_click()`, but holds one or more modifier keys for the duration of the click |
|
|
146
|
+
| `type_string(input_string)` | Types the supplied string character by character with human-like inter-key delays. Handles shift-required characters and special keys (`\n`, `\t`, `\b`) automatically |
|
|
147
|
+
| `toggle_key_preflight_check()` | Ensures toggle keys (CapsLock, ScrollLock, NumLock) are in the desired state before automation begins. Defaults to all off. |
|
|
148
|
+
| `randomize_coordinate_within_range(x, y, radius_x, radius_y)` | Returns a gaussian-randomized `(x, y)` screen coordinate based on a center pixel `(x, y)` and an `x` and `y` radius (total pixels from center on each axis) |
|
|
149
|
+
| `randomize_coordinate_within_square(x, y, radius)` | Same as `randomize_coordinate_within_range()`, but for square-shaped UI elements where `x` and `y` radii are equal |
|
|
150
|
+
| `clamped_gauss_randint(min_int, max_int)` | Returns a gaussian-distributed random integer clamped to `[min_int, max_int]`. Center values are more probable than edge values |
|
|
151
|
+
| `clamped_gauss_randfloat(min_val, max_val)` | Same as `clamped_gauss_randint()`, but returns a float |
|
|
152
|
+
| `start_jitter()` | Causes the mouse cursor to periodically jitter 1-3 pixels, simulating a human hand resting on a mouse. Shares a mutex with `move_mouse()` and will not interfere with it. Resumes automatically after movement completes. Runs indefinitely until `stop_jitter()` is called |
|
|
153
|
+
| `stop_jitter()` | Disables the jitter thread. Call `start_jitter()` again to resume |
|
|
154
|
+
| `rsleep(min_time, max_time)` | Delays execution for a random duration between `min_time` and `max_time` over a clamped gaussian distribution, making center values more common |
|
|
155
|
+
| `rsleep(min_time)` | When called with only `min_time`, the max sleep duration is automatically set to 40% above the supplied value |
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
### Mouse Movement
|
|
159
|
+
|
|
160
|
+
Mouse movement is performed using the `move_mouse()` function. It moves the cursor along a procedurally generated curve starting at the cursor's current position:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
move_mouse(dest_x, dest_y, speed_multiplier=1.0, mouse_hz=500, speed_sigmas_to_edge=3, speed_bias=0.0, jitter_intensity=10)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
# Move mouse to (750, 300)
|
|
168
|
+
move_mouse(750, 300)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### Optional parameters
|
|
172
|
+
* **`speed_multiplier`** `float` - Scales mouse movement speed. A value of `1.2` is 20% faster, `0.5` is half speed. Note that deviating too far from the default of `1.0` may produce visually unnatural
|
|
173
|
+
movement. You do not need to account for travel distance - the function automatically scales speed relative to distance, as humans naturally move slower for short distances and faster for long ones.
|
|
174
|
+
* **`mouse_hz`** `int` - Simulated mouse polling rate. Affects how many points the cursor visits along the movement curve, not the speed of travel. Stick to common polling rates: `125`, `250`, `500`,
|
|
175
|
+
`1000`. Only supply this if you know what you are doing.
|
|
176
|
+
* **`jitter_intensity`** `int` - Controls the intensity of per-point micro-noise applied to the movement curve, simulating natural hand tremor. Higher values produce more visible noise. The noise is
|
|
177
|
+
angle-aligned to the direction of travel at each point, so it looks physically natural rather than random. Scales automatically with movement distance and speed.
|
|
178
|
+
* **`speed_sigmas_to_edge`** `float` - Controls how tightly the randomized speed clusters around the center of the speed range. Higher values produce less variance. See `clamped_gauss_randfloat()` for a
|
|
179
|
+
detailed explanation.
|
|
180
|
+
* **`speed_bias`** `float` - Biases the randomized speed toward the faster or slower end of the range. Accepts values between `-1.0` (bias toward slow) and `1.0` (bias toward fast).
|
|
181
|
+
|
|
182
|
+
### Keyboard Input
|
|
183
|
+
|
|
184
|
+
#### Key Press
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
press_key(key, sigmas_to_edge=3, bias=0.0)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# Press the 'e' key
|
|
192
|
+
press_key('e')
|
|
193
|
+
|
|
194
|
+
# Press the F5 key
|
|
195
|
+
press_key('f5')
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Optional parameters
|
|
199
|
+
|
|
200
|
+
* **`sigmas_to_edge`** `float` - Controls how tightly the randomized key hold duration clusters around the center of the hold range. Higher values produce less variance. See `clamped_gauss_randfloat()`
|
|
201
|
+
for a detailed explanation.
|
|
202
|
+
|
|
203
|
+
* **`bias`** `float` - Biases the randomized hold duration toward the shorter or longer end of the range. Accepts values between `-1.0` (bias toward short) and `1.0` (bias toward long).
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
#### Lagged Key Press
|
|
208
|
+
|
|
209
|
+
Same as `press_key()`, but with randomized delays before and/or after the keypress event. Useful for simulating reaction time before a keypress, or a natural pause after.
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
lagged_press_key(key, prelag=0.1, postlag=0.1, sigmas_to_edge=3, bias=0.0, prelag_sigmas_to_edge=3, prelag_bias=0.0, postlag_sigmas_to_edge=3, postlag_bias=0.0)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
# Press 'e' with default pre and post delays
|
|
217
|
+
lagged_press_key('e')
|
|
218
|
+
|
|
219
|
+
# Press 'e' with a custom pre-delay range of 0.2 to 0.5 seconds
|
|
220
|
+
lagged_press_key('e', prelag=(0.2, 0.5))
|
|
221
|
+
|
|
222
|
+
# Press 'e' with no post-delay
|
|
223
|
+
lagged_press_key('e', postlag=None)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Optional parameters
|
|
227
|
+
|
|
228
|
+
* **`prelag`** `float | tuple[float, float] | None` - Delay before the keypress. A single float sets the minimum, with max automatically set 0.1 seconds higher. A tuple sets an explicit `(min, max)`
|
|
229
|
+
range. Pass `None` to disable.
|
|
230
|
+
* **`postlag`** `float | tuple[float, float] | None` - Delay after the keypress. Behaves identically to `prelag`.
|
|
231
|
+
* **`prelag_sigmas_to_edge`** / **`prelag_bias`** - Controls the distribution of the pre-delay. See `clamped_gauss_randfloat()`.
|
|
232
|
+
* **`postlag_sigmas_to_edge`** / **`postlag_bias`** - Controls the distribution of the post-delay. See `clamped_gauss_randfloat()`.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
#### Modifier Key Press
|
|
237
|
+
|
|
238
|
+
Presses one or more modifier keys, then presses the target key, then releases everything in reverse order with randomized delays between each event.
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
modifier_key_press(modifier, key, min_time=0.03, max_time=0.08, sigmas_to_edge=3, bias=0.0)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
# Ctrl+C
|
|
246
|
+
modifier_key_press('ctrl', 'c')
|
|
247
|
+
|
|
248
|
+
# Ctrl+Shift+T
|
|
249
|
+
modifier_key_press(['ctrl', 'shift'], 't')
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Optional parameters
|
|
253
|
+
|
|
254
|
+
* **`min_time`** / **`max_time`** `float` - The minimum and maximum delay between each modifier down, key press, and modifier up event.
|
|
255
|
+
* **`sigmas_to_edge`** / **`bias`** - Controls the distribution of the inter-event delays. See `clamped_gauss_randfloat()`.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
#### Type String
|
|
260
|
+
|
|
261
|
+
Types a string character by character with human-like inter-key delays. Handles shift-required characters (`!`, `@`, `#`, etc.) and special keys (`\n`, `\t`, `\b`) automatically. CapsLock state is not accounted for. Use `toggle_key_preflight_check()` to ensure it is off before calling if needed.
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
type_string(input_string, speed_multiplier=1.0, sleep_sigmas_to_edge=1.5, sleep_bias=-0.3, hold_sigmas_to_edge=3, hold_bias=0.0)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
type_string("Hello, world!")
|
|
269
|
+
type_string("search query\n")
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Optional parameters
|
|
273
|
+
* **`speed_multiplier`** `float` - Scales the inter-key delay. A value of `1.2` types 20% faster, `0.5` types at half speed.
|
|
274
|
+
* **`sleep_sigmas_to_edge`** / **`sleep_bias`** - Controls the distribution of the delay between keystrokes.
|
|
275
|
+
* **`hold_sigmas_to_edge`** / **`hold_bias`** - Controls the distribution of the key hold duration.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
#### Toggle Key Preflight Check
|
|
280
|
+
|
|
281
|
+
Ensures toggle keys are in the desired state before automation begins. Useful to call at the start of a script to guarantee a known keyboard state.
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
toggle_key_preflight_check(capslock=False, scrolllock=False, numlock=False)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
# Ensure CapsLock and NumLock are off before starting
|
|
289
|
+
toggle_key_preflight_check(capslock=False, numlock=False)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Idle Mouse Behavior
|
|
293
|
+
|
|
294
|
+
When a human hand rests on a mouse, it naturally produces small involuntary movements. `start_jitter()` replicates this behavior by periodically nudging the cursor 1-3 pixels in a random direction while
|
|
295
|
+
idle.
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
start_jitter()
|
|
299
|
+
stop_jitter()
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
# Start idle jitter at the beginning of your script
|
|
304
|
+
start_jitter()
|
|
305
|
+
|
|
306
|
+
# ... automation code ...
|
|
307
|
+
|
|
308
|
+
# Stop jitter when done
|
|
309
|
+
stop_jitter()
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`start_jitter()` and `move_mouse()` share a mutex, so jitter will never interfere with an in-progress mouse movement and will automatically resume once the cursor is no longer in motion. You do not need
|
|
313
|
+
to call `stop_jitter()` before calling `move_mouse()`.
|
|
314
|
+
|
|
315
|
+
`stop_jitter()` permanently disables the jitter thread until `start_jitter()` is called again.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
### Coordinate Randomization
|
|
320
|
+
|
|
321
|
+
Humans do not click the exact center of a UI element every time. These functions return a gaussian-randomized coordinate within a defined area, useful for picking a natural click target within a button
|
|
322
|
+
or other UI element.
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
randomize_coordinate_within_range(x, y, radius_x, radius_y, sigmas_to_edge_x=3, sigmas_to_edge_y=3, bias_x=0.0, bias_y=0.0)
|
|
326
|
+
randomize_coordinate_within_square(x, y, radius, sigmas_to_edge=3, bias_x=0.0, bias_y=0.0)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
# Randomize a click target within a 40x20 pixel button centered at (500, 300)
|
|
331
|
+
x, y = randomize_coordinate_within_range(500, 300, 40, 20)
|
|
332
|
+
move_mouse(x, y)
|
|
333
|
+
left_click()
|
|
334
|
+
|
|
335
|
+
# Same, but for a square element
|
|
336
|
+
x, y = randomize_coordinate_within_square(500, 300, 30)
|
|
337
|
+
move_mouse(x, y)
|
|
338
|
+
left_click()
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`randomize_coordinate_within_square()` is a convenience wrapper for `randomize_coordinate_within_range()` for square-shaped elements where the `x` and `y` radii are equal.
|
|
342
|
+
|
|
343
|
+
#### Optional parameters
|
|
344
|
+
|
|
345
|
+
* **`sigmas_to_edge_x`** / **`sigmas_to_edge_y`** `float` - Controls how tightly the randomized coordinate clusters around the center on each axis. Higher values produce less variance. See `clamped_gauss_randfloat()` for a detailed explanation.
|
|
346
|
+
* **`bias_x`** / **`bias_y`** `float` - Biases the randomized coordinate toward one side of the area on each axis. Accepts values between `-1.0` and `1.0`.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### Timing Utilities
|
|
351
|
+
|
|
352
|
+
#### Random Sleep
|
|
353
|
+
|
|
354
|
+
Delays script execution for a randomized duration over a clamped gaussian distribution, making center values more probable than edge values.
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
rsleep(min_time, max_time=None, sigmas_to_edge=3, bias=0.0)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
# Sleep between 0.5 and 1.5 seconds
|
|
362
|
+
rsleep(0.5, 1.5)
|
|
363
|
+
|
|
364
|
+
# Sleep between 0.5 and 0.7 seconds (max auto-set to 40% above min)
|
|
365
|
+
rsleep(0.5)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
When called with only `min_time`, the max duration is automatically set to 40% above the supplied value.
|
|
369
|
+
|
|
370
|
+
#### Optional parameters
|
|
371
|
+
* **`sigmas_to_edge`** `float` - Controls how tightly the randomized sleep duration clusters around the center of the range. Higher values produce less variance. See `clamped_gauss_randfloat()` for a
|
|
372
|
+
detailed explanation.
|
|
373
|
+
* **`bias`** `float` - Biases the randomized duration toward the shorter or longer end of the range. Accepts values between `-1.0` (bias toward short) and `1.0` (bias toward long).
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
### Statistical Primitives
|
|
378
|
+
|
|
379
|
+
These functions underpin all randomization in the library. They return values over a clamped gaussian distribution, meaning results cluster naturally around the center of the supplied range rather than
|
|
380
|
+
being uniformly distributed. Edge values are possible but rare.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
clamped_gauss_randfloat(min_val, max_val, sigmas_to_edge=3, bias=0.0)
|
|
384
|
+
clamped_gauss_randint(min_int, max_int, sigmas_to_edge=3, bias=0.0)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
# Returns a float between 0.5 and 1.5, center values most likely
|
|
389
|
+
value = clamped_gauss_randfloat(0.5, 1.5)
|
|
390
|
+
|
|
391
|
+
# Returns an integer between 1 and 10, center values most likely
|
|
392
|
+
value = clamped_gauss_randint(1, 10)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### Optional parameters
|
|
396
|
+
* **`sigmas_to_edge`** `float` - Controls the spread of the distribution. Higher values tighten the distribution around the center, making edge values rarer. Lower values flatten it, making edge values
|
|
397
|
+
more common. Defaults to `3`, meaning the edges of the range sit at 3 standard deviations from the mean.
|
|
398
|
+
* **`bias`** `float` - Shifts the center of the distribution toward one end of the range. Accepts values between `-1.0` (bias toward minimum) and `1.0` (bias toward maximum). Defaults to `0.0` (no bias).
|
|
399
|
+
|
|
400
|
+
### Lower-level Control
|
|
401
|
+
While `wabisabio` provides higher-level helpers for common interaction patterns, it also re-exports the underlying `_down` and `_up` primitives from [scanput](https://github.com/sam-howle/scanput). This allows more specialized behavior to be constructed without introducing an additional dependency or import.
|
|
402
|
+
|
|
403
|
+
These primitives can be freely composed with the rest of the `wabisabio` API. Functions such as `key_down(key)`, `key_up(key)`, `left_down()`, `left_up()`, `right_down()`, and `right_up()` make it easy to implement interaction patterns that extend beyond the built-in helpers.
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
from wabisabio import (
|
|
407
|
+
left_down, # Does not require direct import of scanput
|
|
408
|
+
left_up, # Same as above.
|
|
409
|
+
rsleep,
|
|
410
|
+
randomize_coordinate_within_range,
|
|
411
|
+
move_mouse
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
UI_button_x, UI_button_y = 775, 1010
|
|
415
|
+
UI_radius_x, UI_radius_y = 10, 20
|
|
416
|
+
|
|
417
|
+
# Click & drag to a specific, randomized coordinate
|
|
418
|
+
left_down()
|
|
419
|
+
rsleep(0.25, 1.15)
|
|
420
|
+
x, y = randomize_coordinate_within_range(UI_button_x, UI_button_y, UI_radius_x, UI_radius_y)
|
|
421
|
+
move_mouse(x, y, speed_multiplier=1.2)
|
|
422
|
+
rsleep(0.1, 0.25)
|
|
423
|
+
left_up()
|
|
424
|
+
```
|