from sys import exit, argv from os import name as OS_NAME from os import listdir from os.path import exists, join, isdir from time import sleep from typing import Dict try: from click import echo, style except ImportError: print("ERROR Please activate a virtual environment and install requirements.txt") exit() from pyautogui import mouseDown as mouse_down, mouseUp as mouse_up from keyboard import press as key_down, release as key_up, on_release as keyboard_hook # Not entirely sure why this doesn't work #from mouse import press as mouse_down, release as mouse_up, click as mouse_press, move as mouse_move from toml import load as toml_load from asyncio import run, sleep as asleep from asyncio.tasks import gather as task_group def print_err(msg): echo(style("ERROR", fg="bright_red") + " " + msg) def print_warn(msg): echo(style("WARN ", fg="bright_yellow") + " " + msg) def print_ok(msg): echo(style(" OK ", fg="bright_green") + " " + msg) def print_info(msg): print(" " + msg) def keyboard_output(msg: str, delay: int=50, hold: int=20): for k in msg: if k.isupper(): k = k.lower() key_down('shift') key_down(k) sleep(hold / 1000) key_up(k) key_up('shift') sleep(hold / 1000) elif k in ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')']: m = {'!': '1', '@': '2', '#': '3', '$': '4', '%': '5', '^': '6', '&': '7', '*': '8', '(': '9', ')': '0'} key_down('shift') key_down(m[k]) sleep(hold / 1000) key_up(m[k]) key_up('shift') sleep(hold / 1000) elif k in ['\r', '\n']: m = {'\r': 'enter', '\n': 'enter'} key_down(m[k]) sleep(hold / 1000) key_up(m[k]) sleep(hold / 1000) else: key_down(k) sleep(hold / 1000) key_up(k) sleep(hold / 1000) sleep(delay / 1000) def mouse_output(button: str, delay: int=50, hold: int=20): mouse_down(button=button) sleep(hold / 1000) mouse_up(button=button) sleep(hold / 1000) sleep(delay / 1000) platform: str = "" os_name = OS_NAME.lower() if "linux" in os_name or "posix" in os_name: platform = "LINUX" elif "windows" in os_name or "nt" in os_name: platform = "WINDOWS" elif "darwin" in os_name: platform = "MACOS" else: print_err("Platform 'Linux', 'Windows' or 'MacOS' expected") print_info(f"Platform: {OS_NAME}") exit() class Action(): kind: str extra: Dict toggled: bool delay: int max_delay: int def __init__(self, kind, extra=None, toggled=False, delay=0): self.kind = kind self.extra = extra self.toggled = toggled self.delay = delay self.max_delay = delay def toggle(self) -> bool: self.toggled = not self.toggled return self.toggled def tick(self) -> bool: if not self.toggled: if self.delay != self.max_delay: self.delay = self.max_delay return False if self.max_delay != 0: self.delay -= 1 if self.delay <= 0: self.delay = self.max_delay return True return False def do(self, delay: int=50, hold: int=20): if not self.toggled: return d = delay h = hold if self.extra is not None: if "delay" in self.extra: d = self.extra["delay"] if "hold" in self.extra: h = self.extra["hold"] if self.kind == "click": mouse_output(self.extra["button"], d, h) elif self.kind == "click down": mouse_down(self.extra["button"]) elif self.kind == "key": keyboard_output(self.extra["write"], d, h) elif self.kind == "key down": key_down(self.extra["write"]) class Tyrell(): keybinds: Dict[str, Action] toggled: bool delay: int hold: int def __init__(self, profile_name: str): self.profile = { "delay": 50, "hold": 20, "tick": 50, "placeholder_name": False, } self.name = profile_name if profile_name.endswith(".toml"): self.name = profile_name.removesuffix(".toml") if exists(self.name+".toml"): with open(self.name+".toml", "r") as f: try: t = toml_load(f) except Exception as err: print_err(f"Invalid profile '{self.name+'.toml'}'") print(err) exit() self.profile = t if "hold" not in self.profile: self.profile["hold"] = 20 if "delay" not in self.profile: self.profile["delay"] = 50 if "tick" not in self.profile: self.profile["tick"] = 50 if "placeholder_name" not in self.profile: self.profile["placeholder_name"] = False else: print_err(f"Invalid profile '{self.name+'.toml'}'") exit() self.delay = self.profile["delay"] self.hold = self.profile["hold"] self.keybinds = {} self.toggled = False def toggle(self, all: bool=False, all_tickers: bool=False, all_notickers: bool=False) -> bool: self.toggled = not self.toggled if all: for key in self.keybinds: act = self.keybinds[key] act.toggle() elif all_tickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay != 0: act.toggle() elif all_notickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay == 0: act.toggle() return self.toggled def disable(self, all: bool=False, all_tickers: bool=False, all_notickers: bool=False): self.toggled = False if all: for key in self.keybinds: act = self.keybinds[key] act.toggled = False elif all_tickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay != 0: act.toggled = False elif all_notickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay == 0: act.toggled = False def enable(self, all: bool=False, all_tickers: bool=False, all_notickers: bool=False): self.toggled = True if all: for key in self.keybinds: act = self.keybinds[key] act.toggled = True elif all_tickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay != 0: act.toggled = True elif all_notickers: for key in self.keybinds: act = self.keybinds[key] if act.max_delay == 0: act.toggled = True def add_action(self, bind: str, act: Action): act.toggled = False if "placeholder_name" in self.profile: if self.profile["placeholder_name"] and "write" in act.extra: act.extra["write"] = act.extra["write"].replace("{name}", self.name) self.keybinds[bind] = act def remove_action(self, bind: str): self.keybinds[bind] = None def is_action(self, bind: str) -> bool: return self.keybinds[bind] is not None def tick(self): for key in self.keybinds: act = self.keybinds[key] if act.tick(): act.do(self.delay, self.hold) def callback(self, event): key_name = event.name if key_name == "`": if self.toggle(): print_ok("ON") else: print_ok("OFF") elif self.toggled: if key_name in self.keybinds: act = self.keybinds[key_name] if act.max_delay == 0 and act.toggled: print_info(f"{key_name} -> {act.kind}") act.do(self.delay, self.hold) else: print_info(f"{key_name} -> {act.kind} = {act.toggle()}") self.toggle() async def background(self): while True: self.tick() await asleep(self.profile["tick"] / 1000) # 50 ms (20 per second, same as Minecraft) async def mainloop(self): keyboard_hook(self.callback) await task_group( self.background() ) if __name__ == "__main__": if len(argv) == 1: print_err("Missing profile filename") print_info("Example: 'tyrell.py Apollo' would look for 'Apollo.toml'") print() print_info("Profiles allow you to add placeholders,") print_info("And defines in a \"global\" sense key delay and hold delay.") print() exit() ty = Tyrell(",".join(argv[1:])) if not exists("keys"): print_err("Missing 'keys' directory") print_info("(Might want some keybinds)") exit() for ent in listdir("keys"): if ent.startswith(".") or not ent.endswith(".toml") or isdir(ent): continue with open(join("keys", ent), "r") as f: t = toml_load(f) if "duration" in t: ty.add_action(t["keybind"], Action(t["kind"], extra=t, delay=t["duration"])) else: ty.add_action(t["keybind"], Action(t["kind"], extra=t)) print(f"{t['keybind']} -> {t['kind']}") if len(ty.keybinds) == 0: print_err("Missing keybinds") print_info("(Might want some keybinds, place them in a 'keys' directory)") print_info("( Need an example? Look at 'example.toml')") exit() ty.enable(all_notickers=True) ty.disable() try: run(ty.mainloop()) except KeyboardInterrupt: exit()