Last active
March 28, 2024 15:33
-
-
Save jonschoning/8c1c76524283c072c8512967d1a17938 to your computer and use it in GitHub Desktop.
bezmouse
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from time import sleep | |
import mouse | |
import sys | |
def main(): | |
mouse.move_to_area( | |
int(sys.argv[1]), # x | |
int(sys.argv[2]), # y | |
int(sys.argv[3]), # w | |
int(sys.argv[4]), # h | |
int(sys.argv[5]), # dev | |
int(sys.argv[6])) # s | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pyautogui | |
import os, subprocess | |
from time import sleep | |
from random import randint, choice | |
from math import ceil | |
from tools import remove_dups, draw_points | |
from multiprocessing import Process | |
#WORKING DIRECTORY | |
CWD = os.path.dirname(os.path.realpath(__file__)) | |
pyautogui.MINIMUM_DURATION = 0.01 | |
def real_click(): | |
'''This function clicks the mouse with realistic errors: | |
occasional accidental right click | |
occasional double click | |
occasional no click | |
''' | |
if randint(1, 19) != 1: | |
sleep(93 / randint(83,201)) | |
pyautogui.click() | |
else: | |
tmp_rand = randint(1, 3) | |
if tmp_rand == 1: | |
#double click | |
pyautogui.click() | |
sleep(randint(43, 113) / 1000) | |
pyautogui.click() | |
elif tmp_rand == 2: | |
pyautogui.click(button = 'right') | |
def move_to_img(img_name, deviation, speed): | |
''' | |
This function takes the name of an input image (excluding file extension) | |
and moves the mouse to a random pixel on that image. | |
This advanced function saves the xdotool commands to a temporary file | |
'mouse.sh' in ./tmp/ then executes them from the shell to give clean curves | |
This function is very slow because it must identify the image first. It is | |
highly recommended to find the coordinates of the image in a separate thread | |
and feed this into the move() function | |
''' | |
loc = list(pyautogui.locateAllOnScreen(CWD + '/img/' + img_name + '.png')) | |
init_pos = pyautogui.position() | |
if loc: | |
loc = choice(loc) | |
#pick a random one from the list of all occurrences. If there is one occurence, choose that one | |
if loc: | |
x_bounds = loc[0] + randint(0, loc[2]) | |
y_bounds = loc[1] + randint(0, loc[3]) | |
if speed == 0: | |
os.system('xdotool mousemove ' + str(x_bounds) + ' ' + str(y_bounds)) | |
sleep(randint(2,9) / 100) | |
pyautogui.click() | |
else: | |
move(mouse_bez(init_pos, (x_bounds, y_bounds), deviation, speed)) | |
return True | |
else: | |
print("Can't find location") | |
return False | |
def move_to_area(x, y, width, height, deviation, speed): | |
''' | |
Arguments same as pyautogui.locateAllOnScreen format: x and y are top left corner | |
This advanced function saves the xdotool commands to a temporary file | |
'mouse.sh' in ./tmp/ then executes them from the shell to give clean curves | |
''' | |
init_pos = pyautogui.position() | |
x_coord = x + randint(0, width) | |
y_coord = y + randint(0, height) | |
move(mouse_bez(init_pos, (x_coord, y_coord), deviation, speed), False, False) | |
def pascal_row(n): | |
# This returns the nth row of Pascal's Triangle | |
result = [1] | |
x, numerator = 1, n | |
for denominator in range(1, n//2+1): | |
# print(numerator,denominator,x) | |
x *= numerator | |
x /= denominator | |
result.append(x) | |
numerator -= 1 | |
if n&1 == 0: | |
# n is even | |
result.extend(reversed(result[:-1])) | |
else: | |
result.extend(reversed(result)) | |
return result | |
def make_bezier(xys): | |
# xys should be a sequence of 2-tuples (Bezier control points) | |
n = len(xys) | |
combinations = pascal_row(n - 1) | |
def bezier(ts): | |
# This uses the generalized formula for bezier curves | |
# http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization | |
result = [] | |
for t in ts: | |
tpowers = (t**i for i in range(n)) | |
upowers = reversed([(1-t)**i for i in range(n)]) | |
coefs = [c*a*b for c, a, b in zip(combinations, tpowers, upowers)] | |
result.append( | |
list(sum([coef*p for coef, p in zip(coefs, ps)]) for ps in zip(*xys))) | |
return result | |
return bezier | |
def mouse_bez(init_pos, fin_pos, deviation, speed): | |
''' | |
GENERATE BEZIER CURVE POINTS | |
Takes init_pos and fin_pos as a 2-tuple representing xy coordinates | |
variation is a 2-tuple representing the | |
max distance from fin_pos of control point for x and y respectively | |
speed is an int multiplier for speed. The lower, the faster. 1 is fastest. | |
''' | |
#time parameter | |
ts = [t/(speed * 100.0) for t in range(speed * 101)] | |
#bezier centre control points between (deviation / 2) and (deviaion) of travel distance, plus or minus at random | |
control_1 = (init_pos[0] + choice((-1, 1)) * abs(ceil(fin_pos[0]) - ceil(init_pos[0])) * 0.01 * randint(deviation / 2, deviation), | |
init_pos[1] + choice((-1, 1)) * abs(ceil(fin_pos[1]) - ceil(init_pos[1])) * 0.01 * randint(deviation / 2, deviation) | |
) | |
control_2 = (init_pos[0] + choice((-1, 1)) * abs(ceil(fin_pos[0]) - ceil(init_pos[0])) * 0.01 * randint(deviation / 2, deviation), | |
init_pos[1] + choice((-1, 1)) * abs(ceil(fin_pos[1]) - ceil(init_pos[1])) * 0.01 * randint(deviation / 2, deviation) | |
) | |
xys = [init_pos, control_1, control_2, fin_pos] | |
bezier = make_bezier(xys) | |
points = bezier(ts) | |
return points | |
def connected_bez(coord_list, deviation, speed): | |
''' | |
Connects all the coords in coord_list with bezier curve | |
and returns all the points in new curve | |
ARGUMENT: DEVIATION (INT) | |
deviation controls how straight the lines drawn my the cursor | |
are. Zero deviation gives straight lines | |
Accuracy is a percentage of the displacement of the mouse from point A to | |
B, which is given as maximum control point deviation. | |
Naturally, deviation of 10 (10%) gives maximum control point deviation | |
of 10% of magnitude of displacement of mouse from point A to B, | |
and a minimum of 5% (deviation / 2) | |
''' | |
i = 1 | |
points = [] | |
points.append('click') | |
while i < len(coord_list): | |
points += mouse_bez(coord_list[i - 1], coord_list[i], deviation, speed) | |
points.append('click') | |
i += 1 | |
return points | |
def move(mouse_points, draw = False, rand_err = True): | |
''' | |
Moves mouse in accordance with a list of points (continuous curve) | |
Input these as a list of points (2-tuple or another list) | |
Generates file (mouse.sh) in ./tmp/ and runs it as bash file | |
If you want a click at a particular point, write 'click' for that point in | |
mouse_points | |
This advanced function saves the xdotool commands to a temporary file | |
'mouse.sh' in ./tmp/ then executes them from the shell to give clean curves | |
You may wish to generate smooth bezier curve points to input into this | |
function. In this case, take mouse_bez(init_pos, fin_pos, deviation, speed) | |
as the argument. | |
PARAMETERS: | |
mouse_points | |
list of 2-tuples or lists of ints or floats representing xy coords | |
draw | |
a boolean deciding whether or not to draw the curve the mouse makes | |
to a file in /tmp/ | |
''' | |
fname = 'mouse.sh' | |
outfile = open(CWD + '/tmp/' + fname, 'w') | |
os.system('chmod +x ' + CWD + '/tmp/' + fname) | |
outfile.write('#!/bin/bash') | |
outfile.write('\n\n') | |
#draw coords to file in ./tmp/ | |
if draw == True: | |
drawpoints = [(v[0] - REL_ORIGIN[0], v[1] - REL_ORIGIN[1]) for v in mouse_points if type(v) is not str] | |
draw_points(drawpoints, width = 754, height = 503) | |
#round floats to ints | |
mouse_points = [[round(v) for v in x] if type(x) is not str else x for x in mouse_points] | |
for coord in mouse_points: | |
if coord == 'click': | |
if rand_err: | |
tmp = randint(1,39) | |
if tmp == 1: | |
outfile.write('xdotool click 3 \n') | |
elif tmp == 2: | |
outfile.write('xdotool click --repeat 2 1 \n') | |
elif tmp in range(4, 40): #if tmp == 4, write nothing | |
outfile.write('xdotool click 1 \n') #normal click | |
else: | |
outfile.write('xdotool click 1 \n') | |
else: | |
outfile.write('xdotool mousemove ' + str(coord[0]) + ' ' + str(coord[1]) + '\n') | |
outfile.close() | |
subprocess.call([CWD + '/tmp/' + fname]) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
from time import time, sleep | |
from more_itertools import unique_everseen | |
from PIL import Image | |
from random import randint | |
from threading import Timer | |
from time import gmtime, strftime | |
#WORKING DIRECTORY | |
CWD = os.path.dirname(os.path.realpath(__file__)) | |
def remove_dups(seq): | |
return list(unique_everseen(seq)) | |
def draw_points(points, width = 2000, height = 2000): | |
''' | |
Draws yellow crosses to a (default 2000x2000px) image for all coordinates in | |
"points" argument | |
saves to CWD as out-0000.png | |
''' | |
img = Image.new("RGB", (width, height)) | |
pix = img.load() | |
try: | |
for coords in points: | |
pix[coords[0], coords[1]] = (255, 255, 0) | |
pix[coords[0] + 1, coords[1] + 1] = (255, 255, 0) | |
pix[coords[0] + 1, coords[1] - 1] = (255, 255, 0) | |
pix[coords[0] - 1, coords[1] + 1] = (255, 255, 0) | |
pix[coords[0] - 1, coords[1] - 1] = (255, 255, 0) | |
img.save(CWD + '/tmp/out-' + strftime("%Y-%m-%d %H:%M:%S", gmtime()) | |
+ '.png') | |
except: | |
pass | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment