|
@@ -1,37 +1,312 @@
|
|
|
|
|
|
-from sys import exit
|
|
|
-
|
|
|
-if __name__ != "__main__":
|
|
|
- print("Library codebase isn't implemented yet.")
|
|
|
- exit()
|
|
|
+from sys import exit, argv
|
|
|
|
|
|
from os import name as OS_NAME
|
|
|
-# from os import makedirs, removedirs
|
|
|
-#from os.path import exists, join
|
|
|
-from tkinter import Tk, N as tk_N, E as tk_E, S as tk_S, W as tk_W
|
|
|
-from tkinter.ttk import Frame, Label, Button
|
|
|
+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("!!! FATAL ERROR !!!")
|
|
|
- print("Please activate a virtual environment and install requirements.txt")
|
|
|
+ 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 = ""
|
|
|
-if "linux" in OS_NAME.lower():
|
|
|
+os_name = OS_NAME.lower()
|
|
|
+if "linux" in os_name or "posix" in os_name:
|
|
|
platform = "LINUX"
|
|
|
-elif "windows" in OS_NAME.lower() or "nt" in OS_NAME.lower():
|
|
|
+elif "windows" in os_name or "nt" in os_name:
|
|
|
platform = "WINDOWS"
|
|
|
-elif "darwin" in OS_NAME.lower():
|
|
|
+elif "darwin" in os_name:
|
|
|
platform = "MACOS"
|
|
|
else:
|
|
|
- echo(style("ERROR", fg="bright_red") + " Platform 'Linux', 'Windows' or 'MacOS' expected")
|
|
|
- print(f" Platform: {OS_NAME}")
|
|
|
+ print_err("Platform 'Linux', 'Windows' or 'MacOS' expected")
|
|
|
+ print_info(f"Platform: {OS_NAME}")
|
|
|
exit()
|
|
|
|
|
|
-root = Tk()
|
|
|
-root.title("Tyrell")
|
|
|
+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()
|
|
|
+ )
|
|
|
|
|
|
-root.focus()
|
|
|
-root.mainloop()
|
|
|
+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()
|