Skip to content

Instantly share code, notes, and snippets.

@hldh214
Last active February 17, 2020 01:52
Show Gist options
  • Save hldh214/24971ca19d6095f14231d12d39893c6f to your computer and use it in GitHub Desktop.
Save hldh214/24971ca19d6095f14231d12d39893c6f to your computer and use it in GitHub Desktop.
gtav-casino
import logging
import pytesseract
import pywinauto
# Code by Daniel Kukiela (https://twitter.com/daniel_kukiela)
import ctypes
from threading import Thread
from time import sleep
from queue import Queue
import win32gui
def make_relative_rect(source, diff):
return list(map(lambda x: x[0] + x[1], zip(source, diff)))
# main keys class
class Keys(object):
common = None
standalone = False
# instance of worker class
keys_worker = None
keys_process = None
# key constants
direct_keys = 0x0008
virtual_keys = 0x0000
key_press = 0x0000
key_release = 0x0002
# mouse constants
mouse_move = 0x0001
mouse_lb_press = 0x0002
mouse_lb_release = 0x0004
mouse_rb_press = 0x0008
mouse_rb_release = 0x0010
mouse_mb_press = 0x0020
mouse_mb_release = 0x0040
# direct keys
dk = {
"1": 0x02,
"2": 0x03,
"3": 0x04,
"4": 0x05,
"5": 0x06,
"6": 0x07,
"7": 0x08,
"8": 0x09,
"9": 0x0A,
"0": 0x0B,
"NUMPAD1": 0x4F, "NP1": 0x4F,
"NUMPAD2": 0x50, "NP2": 0x50,
"NUMPAD3": 0x51, "NP3": 0x51,
"NUMPAD4": 0x4B, "NP4": 0x4B,
"NUMPAD5": 0x4C, "NP5": 0x4C,
"NUMPAD6": 0x4D, "NP6": 0x4D,
"NUMPAD7": 0x47, "NP7": 0x47,
"NUMPAD8": 0x48, "NP8": 0x48,
"NUMPAD9": 0x49, "NP9": 0x49,
"NUMPAD0": 0x52, "NP0": 0x52,
"DIVIDE": 0xB5, "NPDV": 0xB5,
"MULTIPLY": 0x37, "NPM": 0x37,
"SUBSTRACT": 0x4A, "NPS": 0x4A,
"ADD": 0x4E, "NPA": 0x4E,
"DECIMAL": 0x53, "NPDC": 0x53,
"NUMPADENTER": 0x9C, "NPE": 0x9C,
"A": 0x1E,
"B": 0x30,
"C": 0x2E,
"D": 0x20,
"E": 0x12,
"F": 0x21,
"G": 0x22,
"H": 0x23,
"I": 0x17,
"J": 0x24,
"K": 0x25,
"L": 0x26,
"M": 0x32,
"N": 0x31,
"O": 0x18,
"P": 0x19,
"Q": 0x10,
"R": 0x13,
"S": 0x1F,
"T": 0x14,
"U": 0x16,
"V": 0x2F,
"W": 0x11,
"X": 0x2D,
"Y": 0x15,
"Z": 0x2C,
"F1": 0x3B,
"F2": 0x3C,
"F3": 0x3D,
"F4": 0x3E,
"F5": 0x3F,
"F6": 0x40,
"F7": 0x41,
"F8": 0x42,
"F9": 0x43,
"F10": 0x44,
"F11": 0x57,
"F12": 0x58,
"UP": 0xC8,
"LEFT": 0xCB,
"RIGHT": 0xCD,
"DOWN": 0xD0,
"ESC": 0x01,
"SPACE": 0x39, "SPC": 0x39,
"RETURN": 0x1C, "ENT": 0x1C,
"INSERT": 0xD2, "INS": 0xD2,
"DELETE": 0xD3, "DEL": 0xD3,
"HOME": 0xC7,
"END": 0xCF,
"PRIOR": 0xC9, "PGUP": 0xC9,
"NEXT": 0xD1, "PGDN": 0xD1,
"BACK": 0x0E,
"TAB": 0x0F,
"LCONTROL": 0x1D, "LCTRL": 0x1D,
"RCONTROL": 0x9D, "RCTRL": 0x9D,
"LSHIFT": 0x2A, "LSH": 0x2A,
"RSHIFT": 0x36, "RSH": 0x36,
"LMENU": 0x38, "LALT": 0x38,
"RMENU": 0xB8, "RALT": 0xB8,
"LWIN": 0xDB,
"RWIN": 0xDC,
"APPS": 0xDD,
"CAPITAL": 0x3A, "CAPS": 0x3A,
"NUMLOCK": 0x45, "NUM": 0x45,
"SCROLL": 0x46, "SCR": 0x46,
"MINUS": 0x0C, "MIN": 0x0C,
"LBRACKET": 0x1A, "LBR": 0x1A,
"RBRACKET": 0x1B, "RBR": 0x1B,
"SEMICOLON": 0x27, "SEM": 0x27,
"APOSTROPHE": 0x28, "APO": 0x28,
"GRAVE": 0x29, "GRA": 0x29,
"BACKSLASH": 0x2B, "BSL": 0x2B,
"COMMA": 0x33, "COM": 0x33,
"PERIOD": 0x34, "PER": 0x34,
"SLASH": 0x35, "SLA": 0x35,
}
# virtual keys
vk = {
"1": 0x31,
"2": 0x32,
"3": 0x33,
"4": 0x34,
"5": 0x35,
"6": 0x36,
"7": 0x37,
"8": 0x38,
"9": 0x39,
"0": 0x30,
"NUMPAD1": 0x61, "NP1": 0x61,
"NUMPAD2": 0x62, "NP2": 0x62,
"NUMPAD3": 0x63, "NP3": 0x63,
"NUMPAD4": 0x64, "NP4": 0x64,
"NUMPAD5": 0x65, "NP5": 0x65,
"NUMPAD6": 0x66, "NP6": 0x66,
"NUMPAD7": 0x67, "NP7": 0x67,
"NUMPAD8": 0x68, "NP8": 0x68,
"NUMPAD9": 0x69, "NP9": 0x69,
"NUMPAD0": 0x60, "NP0": 0x60,
"DIVIDE": 0x6F, "NPDV": 0x6F,
"MULTIPLY": 0x6A, "NPM": 0x6A,
"SUBSTRACT": 0x6D, "NPS": 0x6D,
"ADD": 0x6B, "NPA": 0x6B,
"DECIMAL": 0x6E, "NPDC": 0x6E,
"NUMPADENTER": 0x0D, "NPE": 0x0D,
"A": 0x41,
"B": 0x42,
"C": 0x43,
"D": 0x44,
"E": 0x45,
"F": 0x46,
"G": 0x47,
"H": 0x48,
"I": 0x49,
"J": 0x4A,
"K": 0x4B,
"L": 0x4C,
"M": 0x4D,
"N": 0x4E,
"O": 0x4F,
"P": 0x50,
"Q": 0x51,
"R": 0x52,
"S": 0x53,
"T": 0x54,
"U": 0x55,
"V": 0x56,
"W": 0x57,
"X": 0x58,
"Y": 0x59,
"Z": 0x5A,
"F1": 0x70,
"F2": 0x71,
"F3": 0x72,
"F4": 0x73,
"F5": 0x74,
"F6": 0x75,
"F7": 0x76,
"F8": 0x77,
"F9": 0x78,
"F10": 0x79,
"F11": 0x7A,
"F12": 0x7B,
"UP": 0x26,
"LEFT": 0x25,
"RIGHT": 0x27,
"DOWN": 0x28,
"ESC": 0x1B,
"SPACE": 0x20, "SPC": 0x20,
"RETURN": 0x0D, "ENT": 0x0D,
"INSERT": 0x2D, "INS": 0x2D,
"DELETE": 0x2E, "DEL": 0x2E,
"HOME": 0x24,
"END": 0x23,
"PRIOR": 0x21, "PGUP": 0x21,
"NEXT": 0x22, "PGDN": 0x22,
"BACK": 0x08,
"TAB": 0x09,
"LCONTROL": 0xA2, "LCTRL": 0xA2,
"RCONTROL": 0xA3, "RCTRL": 0xA3,
"LSHIFT": 0xA0, "LSH": 0xA0,
"RSHIFT": 0xA1, "RSH": 0xA1,
"LMENU": 0xA4, "LALT": 0xA4,
"RMENU": 0xA5, "RALT": 0xA5,
"LWIN": 0x5B,
"RWIN": 0x5C,
"APPS": 0x5D,
"CAPITAL": 0x14, "CAPS": 0x14,
"NUMLOCK": 0x90, "NUM": 0x90,
"SCROLL": 0x91, "SCR": 0x91,
"MINUS": 0xBD, "MIN": 0xBD,
"LBRACKET": 0xDB, "LBR": 0xDB,
"RBRACKET": 0xDD, "RBR": 0xDD,
"SEMICOLON": 0xBA, "SEM": 0xBA,
"APOSTROPHE": 0xDE, "APO": 0xDE,
"GRAVE": 0xC0, "GRA": 0xC0,
"BACKSLASH": 0xDC, "BSL": 0xDC,
"COMMA": 0xBC, "COM": 0xBC,
"PERIOD": 0xBE, "PER": 0xBE,
"SLASH": 0xBF, "SLA": 0xBF,
}
# setup object
def __init__(self, common=None):
self.keys_worker = KeysWorker(self)
# Thread(target=self.keys_worker.processQueue).start()
self.common = common
if common is None:
self.standalone = True
# parses keys string and adds keys to the queue
def parse_key_string(self, string):
# print keys
if not self.standalone:
self.common.info("Processing keys: %s" % string)
key_queue = []
errors = []
# defaults to direct keys
key_type = self.direct_keys
# split by comma
keys = string.upper().split(",")
# translate
for key in keys:
# up, down or stroke?
up = True
down = True
direction = key.split("_")
subkey = direction[0]
if len(direction) >= 2:
if direction[1] == 'UP':
down = False
else:
up = False
# switch to virtual keys
if subkey == "VK":
key_type = self.virtual_keys
# switch to direct keys
elif subkey == "DK":
key_type = self.direct_keys
# key code
elif subkey.startswith("0x"):
subkey = int(subkey, 16)
if subkey > 0 and subkey < 256:
key_queue.append({
"key": int(subkey),
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
else:
errors.append(key)
# pause
elif subkey.startswith("-"):
time = float(subkey.replace("-", "")) / 1000
if time > 0 and time <= 10:
key_queue.append({
"key": None,
"okey": "",
"time": time,
"up": False,
"down": False,
"type": None,
})
else:
errors.append(key)
# direct key
elif key_type == self.direct_keys and subkey in self.dk:
key_queue.append({
"key": self.dk[subkey],
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
# virtual key
elif key_type == self.virtual_keys and subkey in self.vk:
key_queue.append({
"key": self.vk[subkey],
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
# no match?
else:
errors.append(key)
# if there are errors, do not process keys
if len(errors):
return errors
# create new thread if there is no active one
if self.keys_process is None or not self.keys_process.is_alive():
self.keys_process = Thread(target=self.keys_worker.processQueue)
self.keys_process.start()
# add keys to queue
for i in key_queue:
self.keys_worker.key_queue.put(i)
self.keys_worker.key_queue.put(None)
return True
# direct key press
def directKey(self, key, direction=None, type=None):
if type is None:
type = self.direct_keys
if direction is None:
direction = self.key_press
if key.startswith("0x"):
key = int(key, 16)
else:
key = key.upper()
lookup_table = self.dk if type == self.direct_keys else self.vk
key = lookup_table[key] if key in lookup_table else 0x0000
self.keys_worker.sendKey(key, direction | type)
# direct mouse move or button press
def directMouse(self, dx=0, dy=0, buttons=0):
self.keys_worker.sendMouse(dx, dy, buttons)
def click(self, button='left', repeat=1, repeat_sleep_time=0.1, sleep_time=0.05):
for _ in range(repeat):
sleep(repeat_sleep_time)
self.directMouse(buttons=self.mouse_lb_press if button == 'left' else self.mouse_rb_press)
sleep(sleep_time)
self.directMouse(buttons=self.mouse_lb_release if button == 'left' else self.mouse_rb_release)
def relative_move(self, x, y, sleep_time=0.2):
self.directMouse(-999, -999)
sleep(sleep_time)
self.directMouse(x, y)
# threaded sending keys class
class KeysWorker():
# keys object
keys = None
# queue of keys
key_queue = Queue()
# init
def __init__(self, keys):
self.keys = keys
# main function, process key's queue in loop
def processQueue(self):
# endless loop
while True:
# get one key
key = self.key_queue.get()
# terminate process if queue is empty
if key is None:
self.key_queue.task_done()
if self.key_queue.empty():
return
continue
# print key
elif not self.keys.standalone:
self.keys.common.info(
"Key: \033[1;35m%s/%s\033[0;37m, duration: \033[1;35m%f\033[0;37m, direction: \033[1;35m%s\033[0;37m, type: \033[1;35m%s" % (
key["okey"] if key["okey"] else "None",
key["key"], key["time"],
"UP" if key["up"] and not key["down"] else "DOWN" if not key["up"] and key[
"down"] else "BOTH" if key["up"] and key["down"] else "NONE",
"None" if key["type"] is None else "DK" if key["type"] == self.keys.direct_keys else "VK"),
"\033[0;35mKEY: \033[0;37m"
)
# if it's a key
if key["key"]:
# press
if key["down"]:
self.sendKey(key["key"], self.keys.key_press | key["type"])
# wait
sleep(key["time"])
# and release
if key["up"]:
self.sendKey(key["key"], self.keys.key_release | key["type"])
# not an actual key, just pause
else:
sleep(key["time"])
# mark as done (decrement internal queue counter)
self.key_queue.task_done()
# send key
def sendKey(self, key, type):
self.SendInput(self.Keyboard(key, type))
# send mouse
def sendMouse(self, dx, dy, buttons):
if dx != 0 or dy != 0:
buttons |= self.keys.mouse_move
self.SendInput(self.Mouse(buttons, dx, dy))
# send input
def SendInput(self, *inputs):
nInputs = len(inputs)
LPINPUT = INPUT * nInputs
pInputs = LPINPUT(*inputs)
cbSize = ctypes.c_int(ctypes.sizeof(INPUT))
return ctypes.windll.user32.SendInput(nInputs, pInputs, cbSize)
# get input object
def Input(self, structure):
if isinstance(structure, MOUSEINPUT):
return INPUT(0, _INPUTunion(mi=structure))
if isinstance(structure, KEYBDINPUT):
return INPUT(1, _INPUTunion(ki=structure))
if isinstance(structure, HARDWAREINPUT):
return INPUT(2, _INPUTunion(hi=structure))
raise TypeError('Cannot create INPUT structure!')
# mouse input
def MouseInput(self, flags, x, y, data):
return MOUSEINPUT(x, y, data, flags, 0, None)
# keyboard input
def KeybdInput(self, code, flags):
return KEYBDINPUT(code, code, flags, 0, None)
# hardware input
def HardwareInput(self, message, parameter):
return HARDWAREINPUT(message & 0xFFFFFFFF,
parameter & 0xFFFF,
parameter >> 16 & 0xFFFF)
# mouse object
def Mouse(self, flags, x=0, y=0, data=0):
return self.Input(self.MouseInput(flags, x, y, data))
# keyboard object
def Keyboard(self, code, flags=0):
return self.Input(self.KeybdInput(code, flags))
# hardware object
def Hardware(self, message, parameter=0):
return self.Input(self.HardwareInput(message, parameter))
# types
LONG = ctypes.c_long
DWORD = ctypes.c_ulong
ULONG_PTR = ctypes.POINTER(DWORD)
WORD = ctypes.c_ushort
class MOUSEINPUT(ctypes.Structure):
_fields_ = (('dx', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR))
class KEYBDINPUT(ctypes.Structure):
_fields_ = (('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR))
class HARDWAREINPUT(ctypes.Structure):
_fields_ = (('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD))
class _INPUTunion(ctypes.Union):
_fields_ = (('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT))
class INPUT(ctypes.Structure):
_fields_ = (('type', DWORD),
('union', _INPUTunion))
# create logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
# 0 for borderless mode
# 45 for 4k resolution's windowed mode
height_offset = 45
pytesseract.pytesseract.tesseract_cmd = 'C:/Program Files/Tesseract-OCR/tesseract'
hwnd = win32gui.FindWindow(None, 'Grand Theft Auto V')
win32gui.SetForegroundWindow(hwnd)
pwa_app = pywinauto.application.Application()
gtav_app = pwa_app.connect(handle=hwnd)
gtav_window = gtav_app.window()
keys_obj = Keys()
payout_coords = (1315, 645 + height_offset, 1440, 695 + height_offset)
horse_coords = (
(300, 350 + height_offset),
(300, 460 + height_offset),
(300, 570 + height_offset),
(300, 680 + height_offset),
(300, 790 + height_offset),
(300, 900 + height_offset),
)
def left_click(coords, repeat=1):
gtav_window.click_input(coords=coords)
keys_obj.click('left', repeat)
while True:
sleep(1)
# move to single match's bet button and click it
left_click((1440, 900 + height_offset))
# get min payout horse
payouts = []
for horse_no, horse_coord in enumerate(horse_coords):
left_click(horse_coord)
full_screenshot = gtav_window.capture_as_image()
payout_part = full_screenshot.crop((
1320, 645 + height_offset, 1440, 695 + height_offset
))
payout = pytesseract.image_to_string(payout_part)
try:
payouts.append(int(payout))
except ValueError:
payout_part.show()
raise
min_payout_index = payouts.index(min(payouts))
min_payout_horse_coord = horse_coords[min_payout_index]
logger.info('{}[{}] = {}'.format(payouts, min_payout_index, min(payouts)))
# choose min paid horse
left_click(min_payout_horse_coord)
sleep(1)
# make a bet
left_click((1525, 520 + height_offset), 27)
sleep(1)
# start
left_click((1280, 800 + height_offset))
# wait for the match complete
sleep(40)
# leave result page (or press ESC)
keys_obj.click('right')
# Code by Daniel Kukiela (https://twitter.com/daniel_kukiela)
import ctypes
from threading import Thread
from time import sleep
from queue import Queue
import win32gui
def make_relative_rect(source, diff):
return list(map(lambda x: x[0] + x[1], zip(source, diff)))
# main keys class
class Keys(object):
common = None
standalone = False
# instance of worker class
keys_worker = None
keys_process = None
# key constants
direct_keys = 0x0008
virtual_keys = 0x0000
key_press = 0x0000
key_release = 0x0002
# mouse constants
mouse_move = 0x0001
mouse_lb_press = 0x0002
mouse_lb_release = 0x0004
mouse_rb_press = 0x0008
mouse_rb_release = 0x0010
mouse_mb_press = 0x0020
mouse_mb_release = 0x0040
# direct keys
dk = {
"1": 0x02,
"2": 0x03,
"3": 0x04,
"4": 0x05,
"5": 0x06,
"6": 0x07,
"7": 0x08,
"8": 0x09,
"9": 0x0A,
"0": 0x0B,
"NUMPAD1": 0x4F, "NP1": 0x4F,
"NUMPAD2": 0x50, "NP2": 0x50,
"NUMPAD3": 0x51, "NP3": 0x51,
"NUMPAD4": 0x4B, "NP4": 0x4B,
"NUMPAD5": 0x4C, "NP5": 0x4C,
"NUMPAD6": 0x4D, "NP6": 0x4D,
"NUMPAD7": 0x47, "NP7": 0x47,
"NUMPAD8": 0x48, "NP8": 0x48,
"NUMPAD9": 0x49, "NP9": 0x49,
"NUMPAD0": 0x52, "NP0": 0x52,
"DIVIDE": 0xB5, "NPDV": 0xB5,
"MULTIPLY": 0x37, "NPM": 0x37,
"SUBSTRACT": 0x4A, "NPS": 0x4A,
"ADD": 0x4E, "NPA": 0x4E,
"DECIMAL": 0x53, "NPDC": 0x53,
"NUMPADENTER": 0x9C, "NPE": 0x9C,
"A": 0x1E,
"B": 0x30,
"C": 0x2E,
"D": 0x20,
"E": 0x12,
"F": 0x21,
"G": 0x22,
"H": 0x23,
"I": 0x17,
"J": 0x24,
"K": 0x25,
"L": 0x26,
"M": 0x32,
"N": 0x31,
"O": 0x18,
"P": 0x19,
"Q": 0x10,
"R": 0x13,
"S": 0x1F,
"T": 0x14,
"U": 0x16,
"V": 0x2F,
"W": 0x11,
"X": 0x2D,
"Y": 0x15,
"Z": 0x2C,
"F1": 0x3B,
"F2": 0x3C,
"F3": 0x3D,
"F4": 0x3E,
"F5": 0x3F,
"F6": 0x40,
"F7": 0x41,
"F8": 0x42,
"F9": 0x43,
"F10": 0x44,
"F11": 0x57,
"F12": 0x58,
"UP": 0xC8,
"LEFT": 0xCB,
"RIGHT": 0xCD,
"DOWN": 0xD0,
"ESC": 0x01,
"SPACE": 0x39, "SPC": 0x39,
"RETURN": 0x1C, "ENT": 0x1C,
"INSERT": 0xD2, "INS": 0xD2,
"DELETE": 0xD3, "DEL": 0xD3,
"HOME": 0xC7,
"END": 0xCF,
"PRIOR": 0xC9, "PGUP": 0xC9,
"NEXT": 0xD1, "PGDN": 0xD1,
"BACK": 0x0E,
"TAB": 0x0F,
"LCONTROL": 0x1D, "LCTRL": 0x1D,
"RCONTROL": 0x9D, "RCTRL": 0x9D,
"LSHIFT": 0x2A, "LSH": 0x2A,
"RSHIFT": 0x36, "RSH": 0x36,
"LMENU": 0x38, "LALT": 0x38,
"RMENU": 0xB8, "RALT": 0xB8,
"LWIN": 0xDB,
"RWIN": 0xDC,
"APPS": 0xDD,
"CAPITAL": 0x3A, "CAPS": 0x3A,
"NUMLOCK": 0x45, "NUM": 0x45,
"SCROLL": 0x46, "SCR": 0x46,
"MINUS": 0x0C, "MIN": 0x0C,
"LBRACKET": 0x1A, "LBR": 0x1A,
"RBRACKET": 0x1B, "RBR": 0x1B,
"SEMICOLON": 0x27, "SEM": 0x27,
"APOSTROPHE": 0x28, "APO": 0x28,
"GRAVE": 0x29, "GRA": 0x29,
"BACKSLASH": 0x2B, "BSL": 0x2B,
"COMMA": 0x33, "COM": 0x33,
"PERIOD": 0x34, "PER": 0x34,
"SLASH": 0x35, "SLA": 0x35,
}
# virtual keys
vk = {
"1": 0x31,
"2": 0x32,
"3": 0x33,
"4": 0x34,
"5": 0x35,
"6": 0x36,
"7": 0x37,
"8": 0x38,
"9": 0x39,
"0": 0x30,
"NUMPAD1": 0x61, "NP1": 0x61,
"NUMPAD2": 0x62, "NP2": 0x62,
"NUMPAD3": 0x63, "NP3": 0x63,
"NUMPAD4": 0x64, "NP4": 0x64,
"NUMPAD5": 0x65, "NP5": 0x65,
"NUMPAD6": 0x66, "NP6": 0x66,
"NUMPAD7": 0x67, "NP7": 0x67,
"NUMPAD8": 0x68, "NP8": 0x68,
"NUMPAD9": 0x69, "NP9": 0x69,
"NUMPAD0": 0x60, "NP0": 0x60,
"DIVIDE": 0x6F, "NPDV": 0x6F,
"MULTIPLY": 0x6A, "NPM": 0x6A,
"SUBSTRACT": 0x6D, "NPS": 0x6D,
"ADD": 0x6B, "NPA": 0x6B,
"DECIMAL": 0x6E, "NPDC": 0x6E,
"NUMPADENTER": 0x0D, "NPE": 0x0D,
"A": 0x41,
"B": 0x42,
"C": 0x43,
"D": 0x44,
"E": 0x45,
"F": 0x46,
"G": 0x47,
"H": 0x48,
"I": 0x49,
"J": 0x4A,
"K": 0x4B,
"L": 0x4C,
"M": 0x4D,
"N": 0x4E,
"O": 0x4F,
"P": 0x50,
"Q": 0x51,
"R": 0x52,
"S": 0x53,
"T": 0x54,
"U": 0x55,
"V": 0x56,
"W": 0x57,
"X": 0x58,
"Y": 0x59,
"Z": 0x5A,
"F1": 0x70,
"F2": 0x71,
"F3": 0x72,
"F4": 0x73,
"F5": 0x74,
"F6": 0x75,
"F7": 0x76,
"F8": 0x77,
"F9": 0x78,
"F10": 0x79,
"F11": 0x7A,
"F12": 0x7B,
"UP": 0x26,
"LEFT": 0x25,
"RIGHT": 0x27,
"DOWN": 0x28,
"ESC": 0x1B,
"SPACE": 0x20, "SPC": 0x20,
"RETURN": 0x0D, "ENT": 0x0D,
"INSERT": 0x2D, "INS": 0x2D,
"DELETE": 0x2E, "DEL": 0x2E,
"HOME": 0x24,
"END": 0x23,
"PRIOR": 0x21, "PGUP": 0x21,
"NEXT": 0x22, "PGDN": 0x22,
"BACK": 0x08,
"TAB": 0x09,
"LCONTROL": 0xA2, "LCTRL": 0xA2,
"RCONTROL": 0xA3, "RCTRL": 0xA3,
"LSHIFT": 0xA0, "LSH": 0xA0,
"RSHIFT": 0xA1, "RSH": 0xA1,
"LMENU": 0xA4, "LALT": 0xA4,
"RMENU": 0xA5, "RALT": 0xA5,
"LWIN": 0x5B,
"RWIN": 0x5C,
"APPS": 0x5D,
"CAPITAL": 0x14, "CAPS": 0x14,
"NUMLOCK": 0x90, "NUM": 0x90,
"SCROLL": 0x91, "SCR": 0x91,
"MINUS": 0xBD, "MIN": 0xBD,
"LBRACKET": 0xDB, "LBR": 0xDB,
"RBRACKET": 0xDD, "RBR": 0xDD,
"SEMICOLON": 0xBA, "SEM": 0xBA,
"APOSTROPHE": 0xDE, "APO": 0xDE,
"GRAVE": 0xC0, "GRA": 0xC0,
"BACKSLASH": 0xDC, "BSL": 0xDC,
"COMMA": 0xBC, "COM": 0xBC,
"PERIOD": 0xBE, "PER": 0xBE,
"SLASH": 0xBF, "SLA": 0xBF,
}
# setup object
def __init__(self, common=None):
self.keys_worker = KeysWorker(self)
# Thread(target=self.keys_worker.processQueue).start()
self.common = common
if common is None:
self.standalone = True
# parses keys string and adds keys to the queue
def parseKeyString(self, string):
# print keys
if not self.standalone:
self.common.info("Processing keys: %s" % string)
key_queue = []
errors = []
# defaults to direct keys
key_type = self.direct_keys
# split by comma
keys = string.upper().split(",")
# translate
for key in keys:
# up, down or stroke?
up = True
down = True
direction = key.split("_")
subkey = direction[0]
if len(direction) >= 2:
if direction[1] == 'UP':
down = False
else:
up = False
# switch to virtual keys
if subkey == "VK":
key_type = self.virtual_keys
# switch to direct keys
elif subkey == "DK":
key_type = self.direct_keys
# key code
elif subkey.startswith("0x"):
subkey = int(subkey, 16)
if subkey > 0 and subkey < 256:
key_queue.append({
"key": int(subkey),
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
else:
errors.append(key)
# pause
elif subkey.startswith("-"):
time = float(subkey.replace("-", "")) / 1000
if time > 0 and time <= 10:
key_queue.append({
"key": None,
"okey": "",
"time": time,
"up": False,
"down": False,
"type": None,
})
else:
errors.append(key)
# direct key
elif key_type == self.direct_keys and subkey in self.dk:
key_queue.append({
"key": self.dk[subkey],
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
# virtual key
elif key_type == self.virtual_keys and subkey in self.vk:
key_queue.append({
"key": self.vk[subkey],
"okey": subkey,
"time": 0,
"up": up,
"down": down,
"type": key_type,
})
# no match?
else:
errors.append(key)
# if there are errors, do not process keys
if len(errors):
return errors
# create new thread if there is no active one
if self.keys_process is None or not self.keys_process.isAlive():
self.keys_process = Thread(target=self.keys_worker.processQueue)
self.keys_process.start()
# add keys to queue
for i in key_queue:
self.keys_worker.key_queue.put(i)
self.keys_worker.key_queue.put(None)
return True
# direct key press
def directKey(self, key, direction=None, type=None):
if type is None:
type = self.direct_keys
if direction is None:
direction = self.key_press
if key.startswith("0x"):
key = int(key, 16)
else:
key = key.upper()
lookup_table = self.dk if type == self.direct_keys else self.vk
key = lookup_table[key] if key in lookup_table else 0x0000
self.keys_worker.sendKey(key, direction | type)
# direct mouse move or button press
def directMouse(self, dx=0, dy=0, buttons=0):
self.keys_worker.sendMouse(dx, dy, buttons)
def click(self, button='left', repeat=1, repeat_sleep_time=0.1, sleep_time=0.05):
for _ in range(repeat):
sleep(repeat_sleep_time)
self.directMouse(buttons=self.mouse_lb_press if button == 'left' else self.mouse_rb_press)
sleep(sleep_time)
self.directMouse(buttons=self.mouse_lb_release if button == 'left' else self.mouse_rb_release)
def relative_move(self, x, y, sleep_time=0.2):
self.directMouse(-999, -999)
sleep(sleep_time)
self.directMouse(x, y)
# threaded sending keys class
class KeysWorker():
# keys object
keys = None
# queue of keys
key_queue = Queue()
# init
def __init__(self, keys):
self.keys = keys
# main function, process key's queue in loop
def processQueue(self):
# endless loop
while True:
# get one key
key = self.key_queue.get()
# terminate process if queue is empty
if key is None:
self.key_queue.task_done()
if self.key_queue.empty():
return
continue
# print key
elif not self.keys.standalone:
self.keys.common.info(
"Key: \033[1;35m%s/%s\033[0;37m, duration: \033[1;35m%f\033[0;37m, direction: \033[1;35m%s\033[0;37m, type: \033[1;35m%s" % (
key["okey"] if key["okey"] else "None",
key["key"], key["time"],
"UP" if key["up"] and not key["down"] else "DOWN" if not key["up"] and key[
"down"] else "BOTH" if key["up"] and key["down"] else "NONE",
"None" if key["type"] is None else "DK" if key["type"] == self.keys.direct_keys else "VK"),
"\033[0;35mKEY: \033[0;37m"
)
# if it's a key
if key["key"]:
# press
if key["down"]:
self.sendKey(key["key"], self.keys.key_press | key["type"])
# wait
sleep(key["time"])
# and release
if key["up"]:
self.sendKey(key["key"], self.keys.key_release | key["type"])
# not an actual key, just pause
else:
sleep(key["time"])
# mark as done (decrement internal queue counter)
self.key_queue.task_done()
# send key
def sendKey(self, key, type):
self.SendInput(self.Keyboard(key, type))
# send mouse
def sendMouse(self, dx, dy, buttons):
if dx != 0 or dy != 0:
buttons |= self.keys.mouse_move
self.SendInput(self.Mouse(buttons, dx, dy))
# send input
def SendInput(self, *inputs):
nInputs = len(inputs)
LPINPUT = INPUT * nInputs
pInputs = LPINPUT(*inputs)
cbSize = ctypes.c_int(ctypes.sizeof(INPUT))
return ctypes.windll.user32.SendInput(nInputs, pInputs, cbSize)
# get input object
def Input(self, structure):
if isinstance(structure, MOUSEINPUT):
return INPUT(0, _INPUTunion(mi=structure))
if isinstance(structure, KEYBDINPUT):
return INPUT(1, _INPUTunion(ki=structure))
if isinstance(structure, HARDWAREINPUT):
return INPUT(2, _INPUTunion(hi=structure))
raise TypeError('Cannot create INPUT structure!')
# mouse input
def MouseInput(self, flags, x, y, data):
return MOUSEINPUT(x, y, data, flags, 0, None)
# keyboard input
def KeybdInput(self, code, flags):
return KEYBDINPUT(code, code, flags, 0, None)
# hardware input
def HardwareInput(self, message, parameter):
return HARDWAREINPUT(message & 0xFFFFFFFF,
parameter & 0xFFFF,
parameter >> 16 & 0xFFFF)
# mouse object
def Mouse(self, flags, x=0, y=0, data=0):
return self.Input(self.MouseInput(flags, x, y, data))
# keyboard object
def Keyboard(self, code, flags=0):
return self.Input(self.KeybdInput(code, flags))
# hardware object
def Hardware(self, message, parameter=0):
return self.Input(self.HardwareInput(message, parameter))
# types
LONG = ctypes.c_long
DWORD = ctypes.c_ulong
ULONG_PTR = ctypes.POINTER(DWORD)
WORD = ctypes.c_ushort
class MOUSEINPUT(ctypes.Structure):
_fields_ = (('dx', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR))
class KEYBDINPUT(ctypes.Structure):
_fields_ = (('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR))
class HARDWAREINPUT(ctypes.Structure):
_fields_ = (('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD))
class _INPUTunion(ctypes.Union):
_fields_ = (('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT))
class INPUT(ctypes.Structure):
_fields_ = (('type', DWORD),
('union', _INPUTunion))
# example:
if __name__ == '__main__':
hwnd = win32gui.FindWindow(None, 'Grand Theft Auto V')
win32gui.SetForegroundWindow(hwnd)
keys = Keys()
while True:
sleep(1)
# move to single match's bet button and click it
keys.relative_move(600, 480)
keys.click()
sleep(1)
# select first horse
keys.relative_move(200, 190)
keys.click()
sleep(1)
# make a bet
keys.relative_move(600, 290)
keys.click('left', 28)
sleep(1)
# start
keys.relative_move(550, 440)
keys.click()
# wait for the match complete
sleep(40)
# leave result page (or press ESC)
keys.click('right')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment