PK!beeve/__init__.py"""eeve - A flexible, powerfull and simple event trigger""" __version__ = '1.0.0' __author__ = 'Victor Marcelino ' __all__ = [] import travel_backpack from travel_backpack import check_and_raise import os import eeve.helpers as helpers from eeve.importer import import_from_folder from eeve.wrapper import action_wrapper from eeve.base_classes import Action, ActionTemplate, Trigger, TriggerTemplate, Event, Task import eeve.mappings as mappings from typing import Union, Any, Dict, List, Callable trigger_templates: Dict[str, TriggerTemplate] = {} action_templates: Dict[str, ActionTemplate] = {} events: List[Event] = [] def main(): print() script_root = os.path.dirname(os.path.realpath(__file__)) load_triggers_from_path(os.path.join(script_root, 'eeve triggers')) load_actions_from_path(os.path.join(script_root, 'eeve actions')) load_triggers_from_path(os.path.join(script_root, 'eeve plugins')) load_actions_from_path(os.path.join(script_root, 'eeve plugins')) with open(os.path.join(script_root, 'eeve events.txt')) as f: _all_events = f.read().replace('|||\n', '').split('\n') load_events(_all_events) def add_trigger_template(name: str, trigger: Union[Trigger, TriggerTemplate, dict, Callable]): """Adds a TriggerTemplate that can be used as base to a Trigger instance Arguments: name {str} -- the name of the TriggerTemplate trigger {Union[Trigger, TriggerTemplate, dict, Callable]} -- The trigger information. It can be a class where init will be used as register and attribute unregister as the unregister function, a dict with keys `register` and `unregister` with callables as value, a Trigger or another TriggerTemplate. Only the name given on the argument will be used. Raises: AttributeError: when a class passed as "trigger" argument has no "unregister" attribute """ if type(trigger) is Trigger: tt = TriggerTemplate(name=name, register=trigger.register, unregister=trigger._unregister) trigger_templates[name] = tt elif type(trigger) is TriggerTemplate: tt = TriggerTemplate(name=name, register=trigger.register, unregister=trigger.unregister) trigger_templates[name] = tt elif type(trigger) is dict: trigger_templates[name] = TriggerTemplate(name=name, register=trigger['register'], unregister=trigger['unregister']) else: check_and_raise(hasattr(trigger, 'unregister'), 'trigger object must have "unregister" attribute', AttributeError) add_trigger_template(name=name, trigger={'register': trigger, 'unregister': trigger.unregister}) def remove_trigger_template(name: str, unregister=False): """Removes a TriggerTemplate from eeve Warning: only the template will be removed from eeve's system, but any trigger created from it will still function.\n In some cases the unregister function can be called to stop the instance running. This is, however, up to the trigger developer to implement. Arguments: name {str} -- Name of TriggerTemplate to remove Keyword Arguments: unregister {bool} -- if True, will call the unregister function from trigger (default: {False}) """ if name in trigger_templates: if unregister: trigger_templates[name].unregister() del trigger_templates[name] def add_action_template(name: str, action_info: Union[Action, ActionTemplate, dict, Callable], *action_init_args, **action_init_kwargs): """Adds an ActionTemplate that can be used as base to an Action instance Arguments: name {str} -- The name of the ActionTemplate action_info {Union[Action, ActionTemplate, dict, Callable]} -- The action information. It can be a class where init will be used as initialization and attribute "run" as the action function, a dict where key `run` has value of the action function, an optional `init` key with the initialization function as value, an Action or another ActionTemplate. Only the name given on the argument will be used. """ if type(action_info) in [Action, ActionTemplate]: at = ActionTemplate.copy_from(action_info) at.reinitialize_with_args(*action_init_args, **action_init_kwargs) action_templates[name] = at else: action_templates[name] = ActionTemplate.make(name=name, action_info=action_info, *action_init_args, **action_init_kwargs) def remove_action_template(name: str): """Removes an ActionTemplate from eeve Arguments: name {str} -- Name o ActionTemplate to remove """ if name in action_templates: del action_templates[name] def load_triggers_from_path(path): print('--loading triggers--') modules = import_from_folder(path) for module in modules: try: triggers = getattr(module, 'triggers', dict()) for trigger_name, trigger in triggers.items(): print('loading', trigger_name) add_trigger_template(name=trigger_name, trigger=trigger) except Exception as ex: print('invalid trigger module:', ex) print('--triggers loaded--') print() print() def load_actions_from_path(path): print('--loading actions--') modules = import_from_folder(path) for module in modules: try: actions = getattr(module, 'actions', dict()) for action_name, action in actions.items(): print('loading', action_name) add_action_template(name=action_name, action_info=action) except Exception as ex: print('invalid action module:', ex) print('--actions loaded--') print() print() def load_events(_all_events: list): print('--loading events--') for event in _all_events: if event and not event.startswith('#'): show_traceback = False print(f'loading [{event}]') if event.startswith('[test]'): show_traceback = True event = event[len('[test]'):] try: event = mappings.remap(event) trigger, raw_actions = helpers.strip_split(event, mappings.char_map['->'], maxsplit=1) trigger, trigger_args, trigger_kwargs = helpers.process_args(trigger, return_init_args=False) raw_actions = helpers.strip_split(raw_actions, mappings.char_map[';']) actions = [] for action in raw_actions: action_name, action_init_args, action_init_kwargs, action_run_args, action_run_kwargs = helpers.process_args( action, return_init_args=True) _action_template = ActionTemplate.copy_from(action_templates[action_name]) _action_template.reinitialize_with_args(*action_init_args, **action_init_kwargs) _action = Action(*action_run_args, action_info=_action_template, **action_run_kwargs) actions.append(_action) task = Task(actions, debug=show_traceback) trigger = Trigger.make(*trigger_args, template=trigger_templates[trigger], **trigger_kwargs) # pre-initializes the trigger event = Event(triggers=[trigger], task=task) # starts the trigger events.append(event) except Exception as ex: show_traceback = True print('invalid event:', (ex if not show_traceback else travel_backpack.format_exception_string(ex))) print('--events loaded--') print() print()PK!feeve/__main__.pyfrom eeve import main main()PK! TTeeve/base_classes.pyfrom dataclasses import dataclass from typing import Union, Any, Callable, List, Tuple from eeve.wrapper import action_wrapper from travel_backpack import check_and_raise, except_and_print @dataclass class ActionTemplate: name: str func: Callable init_result: Any = None task_info_getter: Union[Callable, None] = None init_func: Union[Callable, None] = None init_class: Union[Callable, None] = None @classmethod def make(cls, *action_init_args, name, action_info: Union[dict, Callable], **action_init_kwargs) -> 'ActionTemplate': self = cls(name=name, func=None) self.init_class = None if type(action_info) is dict: if 'class' in action_info: self.init_class = action_info['class'] else: check_and_raise('run' in action_info, 'action_info must contain a "run" key with a callable as value', KeyError) self.func = action_info['run'] if 'init' in action_info: self.init_func = action_info['init'] self.task_info_getter = action_info.get('task_info', None) else: self.init_class = action_info if self.init_class: self.init_func = self.init_class if self.init_func: self.init_result = self.init_func(*action_init_args, **action_init_kwargs) if self.init_class: check_and_raise(hasattr(self.init_class, 'run'), 'action_info must contain a "run" attribute', AttributeError) self.func = self.init_result.run return self @classmethod def copy_from(cls, obj) -> 'ActionTemplate': attrs = ['name', 'func', 'init_result', 'task_info_getter', 'init_func', 'init_class'] r = {} for attr in attrs: if hasattr(obj, attr): r[attr] = getattr(obj, attr) return cls(**r) def reinitialize_with_args(self, *action_init_args, **action_init_kwargs): check_and_raise(self.init_func, 'Cannot initialize without an initialization function. Please provide an "init" key or a class on action_info', KeyError) self.init_result = self.init_func(*action_init_args, **action_init_kwargs) if self.init_class: self.func = self.init_result.run @dataclass class Action: ''' A function with predetermined args that is executed in a task environment and raises no exceptions ''' name: str func: Callable run_args: Union[list, tuple] run_kwargs: dict init_result: Any = None task_info_getter: Callable = None def run(self, *args, **kwargs): return except_and_print(self.func)(*args, **kwargs) def update_task_info(self, task_info) -> None: if self.task_info_getter is not None: self.task_info_getter(self.init_result, task_info) def __init__(self, *action_run_args, action_info: Union[ActionTemplate, dict, object], name=None, **action_run_kwargs): if type(action_info) is not ActionTemplate: check_and_raise(name, "parameter 'name' must be set when 'action_info' is not of type ActionTemplate", NameError) action_info = ActionTemplate.make(name=name, action_info=action_info) self.name = action_info.name self.func = action_info.func self.run_args = action_run_args self.run_kwargs = action_run_kwargs self.init_result = action_info.init_result self.task_info_getter = action_info.task_info_getter @dataclass # Yeah, I know... This class is horrible and shouldn't even exist. class Task: # I just wanted it to be organized as all the other classes """ A list of Actions that are executed in sequence and in a separate thread. This also carries variable information, which can be of one of the three types: Global Variables: ---------------- \tVariables that are available to any \taction at any given moment. \tThese variables persist until the process ends. Task Variables: -------------- \tVariables that are only available to \tactions inside it's task at any given moment. \tThese variables persist after the task ends, but \tare released when the process ends. \tThis means that one action can access a \tvariable created by an older run of the task. Local Variables: --------------- \tVariables that are only available to \tactions inside it's task when it is running. \tThese variables are released when the task or \tthe process ends. """ start: Callable def __init__(self, actions: List[Action], debug: bool = False): self.start = action_wrapper(actions=actions, debug=debug) @dataclass class TriggerTemplate: name: str register: Callable unregister: Callable @dataclass class Trigger: name: str _register: Callable register: Callable unregister: Callable args: list kwargs: dict def __init__(self, *args, name: str, register: Callable, unregister: Callable, **kwargs): self.name = name self.args = args self.kwargs = kwargs self._register = register self.register = lambda task: register(task, *self.args, **self.kwargs) self.unregister = unregister @classmethod def make(cls, *args, template: TriggerTemplate, **kwargs) -> 'Trigger': return cls(*args, name=template.name, register=template.register, unregister=template.unregister, **kwargs) @dataclass class Event: '''Links triggers with a task and starts all the triggers Is recommended to use a single trigger for each event.\n Multiple triggers are supported for convenience so that all triggers can be unregistered at once for a given task. ''' unregister_info: List[Tuple[Callable, Any]] def __init__(self, triggers: List[Trigger], task: Task): self.unregister_info = [] for trigger in triggers: self.unregister_info.append((trigger.unregister, trigger.register(task.start))) def unregister(self): for unregister_func, trigger_output_result in self.unregister_info: unregister_func(trigger_output_result)PK!uE..$eeve/eeve actions/changePowerPlan.pyimport subprocess import sys mappings = None def get_power_plan_info(print_to_screen=True): global mappings proc = subprocess.Popen(["powercfg", "/l"], stdout=subprocess.PIPE, shell=True) (out, err) = proc.communicate() result = str(out, 'utf-8').replace('\r', '').split('\n')[3:][:-1] result = [r.split(' ', 4)[3:] for r in result] mappings = {} active_plan = None for i in range(len(result)): temp_result = result[i][1].split('(', 1)[1:][0].split(')') result[i][1] = temp_result[0] mappings[result[i][1]] = result[i][0] if temp_result[1].replace(' ', '') == '*': active_plan = temp_result[0] if print_to_screen: print(">>", result[i]) return mappings, active_plan def set_power_plan(power_plan_name): if mappings is None: get_power_plan_info(print_to_screen=False) proc = subprocess.Popen(["powercfg", "/s", mappings[power_plan_name]], stdout=subprocess.PIPE, shell=True) (out, err) = proc.communicate() result2 = str(out, 'utf-8').replace('\r', '') #print(result2) if __name__ == '__main__': _, active_plan = get_power_plan_info() print("Currently active:", active_plan) plan = input("power mode: ") if len(sys.argv) < 2 else sys.argv[1] set_power_plan(plan) _, active_plan = get_power_plan_info(print_to_screen=False) print("Set active:", active_plan) class SetPowerPlan: def run(self, plan_name): _, active_plan = get_power_plan_info(print_to_screen=False) print("Last power plan:", active_plan) set_power_plan(plan_name) _, active_plan = get_power_plan_info(print_to_screen=False) print("Current power plan:", active_plan) actions = {'set power plan': SetPowerPlan} PK!պ eeve/eeve actions/if_action.pyfrom eeve.taskinfo import TaskInfo class IfAction: def run(self, *args, logic='AND', inverse=False, **kwargs): #print('if statement') self.logic = logic.lower() if hasattr(self, 'info'): name = self.info.actions[self.info.current_action_index].name else: name = 'end if' if (name == 'if' or name == 'else') and name != 'end if': result = self.check_args(args, kwargs) if name == 'else': result = False if inverse: result = not result if not result: # if condition resolves to false if_count = 1 for i, action in self.info.get_next_actions(): if action.name == 'else': if_count -= 1 elif action.name == 'end if': if_count -= 1 elif action.name == 'if': if_count += 1 print('\t', i, action.name, '- lv', if_count) if if_count == 0: self.info.current_action_index = i print(f' ==> jump to {i}:', action.name) if action.name == 'else': self.info.current_action_index = i + 1 break else: # if didn't hit break statement self.info.current_action_index = len(self.info.actions) self.info.increment_action_index = False def set_task_info(self, info: TaskInfo): #print('info set') self.info = info def check_args(self, args, kwargs): resolves = [] for arg in args: resolves.append(bool(arg)) for k, v in kwargs.items(): resolves.append(self.info.get_var(k) == self.info.get_var(v)) mapping = {'and': (True, lambda x, y: x and y), 'or': (False, lambda x, y: x or y), 'xor': (False, lambda x, y: x ^ y)} result = mapping.get(self.logic, [True])[0] for i in resolves: result = mapping[self.logic][1](result, i) return result actions = { 'if': { 'class': IfAction, 'task_info': IfAction.set_task_info }, 'else': { 'class': IfAction, 'task_info': IfAction.set_task_info }, 'end if': IfAction } PK!yUeeve/eeve actions/list_dir.pyimport os def run(path, return_full_path=False): result = os.listdir(path) if return_full_path: for i, f in enumerate(result): result[i] = os.path.join(path, f) return {'file_list': result} actions = {"list dir": {'run': run}} PK!]eeve/eeve actions/play_audio.pyimport simpleaudio as sa class PlayAudio: def run(self, audiofile): waveobject = sa.WaveObject.from_wave_file(audiofile) p = waveobject.play() actions = {'play audio': PlayAudio} PK!qiِ#eeve/eeve actions/random_actions.pyimport random def get_random(lst: list): return {'result': random.choice(lst)} actions = {"choose random": {'run': get_random}} PK!ׄYY!eeve/eeve actions/REST_request.pyimport requests import time import travel_backpack class Request: def run(self, method, url, *args, retry_count=-1, wait_time=5, asynchronous=True, **kwargs): if asynchronous: travel_backpack.thread_encapsulation(self.run)( method, url, *args, retry_count=retry_count, wait_time=wait_time, asynchronous=False, **kwargs) return else: retry = True print('REST request:', method, url, args, kwargs) while retry: try: requests.request(method, url) retry = False except requests.exceptions.ConnectionError as e: #print(e) print('.') if retry_count == 0: retry = False else: retry_count -= 1 time.sleep(wait_time) retry = True actions = {'rest request': Request} if __name__ == '__main__': import sys Request().run(*sys.argv[1:])PK!"eeve/eeve actions/start_process.pyimport subprocess def run(*args, windowed=False, **kwargs): if windowed: kwargs['creationflags'] = subprocess.CREATE_NEW_CONSOLE print('start process:', args, kwargs) subprocess.Popen(args, **kwargs) actions = {'start process': {'run': run}} PK!' v*OO eeve/eeve actions/test_action.pyfrom eeve.variable import Variable def init(*args, **kwargs): print('initialization args:', args, kwargs) def run(*args, index=0, **kwargs): index_is_var = hasattr(index, 'value') idx = index.value if index_is_var else index print(f'run args ({idx}):', args, kwargs, end=' ') try: if index_is_var: index.value += 1 else: index += 1 except Exception as e: print(e, end='') index = 1 print() return {'index': index} actions = {'test action': {'init': init, 'run': run}} PK!@4>> eeve/eeve actions/wait_action.pyfrom time import sleep actions = {'wait': {'run': sleep}} PK!;eeve/eeve events.txt#[test]timer: 2 -> test action: init param 1, init param 2, init_par1=True, init_par2=False, init_par3= teste teste | trigger_result=$return_result, run param 1, run param2, run_par1=True, run_par2=False, run_par3= teste teste #[test]timer: 5, start=true -> test action: trigger_result=$return_result, index=$index; test action: trigger_result=$return_result, index=$index; test action: trigger_result=$return_result, index=$index #timer: 5, start=true -> test action: locals=$vars, protected=$$vars, public=$$$vars, index=$index; test action: locals=$vars, protected=$$vars, public=$$$vars, index=$index; test action: locals=$vars, protected=$$vars, public=$$$vars, index=$index; test action: locals=$vars, protected=$$vars, public=$$$vars, index=$index; test action: locals=$vars, protected=$$vars, public=$$$vars, index=$index #---------------------------------------------------------------------------------- display: off -> ||| set power plan: Power saver; ||| rest request: GET, http\://192.168.10.112/?function\=led1_off, retry_count=3;||| rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.000000, retry_count=5 ||| #---------------------------------------------------------------------------------- keys down: RcontrolkeyRshiftkeyEnter, True -> toggle keychain mode: any keychain: Numpad0, True, any -> rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.000000, retry_count=5;rest request: GET, http\://192.168.10.11\:85/TV\;0, retry_count=5; set TP-Link device property: device=Bedroom Light, is_on=False keychain: FAN, True, any -> rest request: GET, http\://192.168.10.11\:85/TV, retry_count=5 keychain: VENT, True, any -> rest request: GET, http\://192.168.10.11\:85/TV, retry_count=5 keychain: LED, True, any -> rest request: GET, http\://192.168.10.11\:85/Perimetral_Led, retry_count=5 keychain: Up, True, any -> rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.FA7100, retry_count=5 keychain: Down, True, any -> rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.000000, retry_count=5 keychain: DAY, True, any -> set TP-Link device property: device=Bedroom Light, is_on=True, brightness=100, temperature=6500 keychain: MOVIE, True, any -> set TP-Link device property: device=Bedroom Light, is_on=True, brightness=0, temperature=2700 # display: on -> start process: cmd, /C, echo oi&&pause, windowed=True #---------------------------------------------------------------------------------------------------- display: on -> ||| set power plan: Balanced; ||| rest request: GET, http\://192.168.10.112/?function\=led1_on, wait_time=0.5, retry_count=15; ||| rest request: GET, http\://192.168.10.11\:2280/surveillance/stop, wait_time=1; ||| rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.FA7100, retry_count=5 ||| #---------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------- session end -> ||| rest request: GET, http\://192.168.10.11\:2280/surveillance/start, retry_count=3, wait_time=1; ||| rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.000000, retry_count=5 #-------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------- system suspend -> ||| rest request: GET, http\://192.168.10.11\:2280/surveillance/start, retry_count=3, wait_time=1; ||| rest request: GET, http\://192.168.10.11\:85/Perimetral_Led.000000, retry_count=5 #-------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------- TP-Link device: connect -> ||| wait: 0.2; ||| if:$is_on; ||| set TP-Link device property: device=$device, is_on=False; ||| wait: 0.5; ||| set TP-Link device property: device=$device, is_on=True; ||| else; ||| set TP-Link device property: device=$device, is_on=True; ||| wait: 0.5; ||| set TP-Link device property: device=$device, is_on=False; ||| end if ||| #-------------------------------------------------------------------- PK!keeve/eeve eventsssss.txtkey down -> test action: key_____1=$vars key down -> test action: key__2=$vars mouse down -> ||| list dir:C\:/Users/Victor Marcelino/Sync/Neurobrinq/IUP6D/IncludeOnBuild/Libs/Audio/aaa, true;||| choose random:$file_list; test action: mouse=$vars;||| play audio:$result key down-> ||| list dir:C\:/Users/Victor Marcelino/Sync/Neurobrinq/IUP6D/IncludeOnBuild/Libs/Audio/aaa, true;||| choose random:$file_list; test action: mouse=$vars;||| play audio:$resultPK!>T__!eeve/eeve plugins/tp-interface.pyfrom pyHS100 import Discover import travel_backpack import time class SmartDeviceContainer: def __init__(self, dev): self.device = dev self.hw_id = self.device.hw_info['hwId'] self.alias = self.device.alias self.disconnect_count = 0 def __hash__(self): h = hash(self.hw_id) #print('hash:', h) return h def __eq__(self, other): return self.hw_id == other.hw_id def __repr__(self): alias = self.alias + ' (offline)' try: alias = self.device.alias except: pass return alias class Discoverer(metaclass=travel_backpack.Singleton): def __init__(self): print('inited') self.timeout = 1 self.time_between_pings = 0 self.threshold = 10 self.single_connect_event = dict() self.single_disconnect_event = dict() self.multi_connect_event = dict() self.multi_disconnect_event = dict() self.discoverer_thread = self.discoverer() print('end init') def set_params(self, timeout, time_between_pings, threshold): self.timeout = timeout self.time_between_pings = time_between_pings self.threshold = threshold @travel_backpack.threadpool def discoverer(self): self.current_device_set = set() while True: #print('\nloop start') new_device_set = set() for ip, dev in Discover.discover(timeout=self.timeout).items(): try: dev = SmartDeviceContainer(dev) new_device_set.add(dev) except: pass offline_devices = self.current_device_set - new_device_set #print('offline devices:'.ljust(18), (offline_devices) or '-') for dev in offline_devices: if dev.disconnect_count > self.threshold: print(dev, 'disconnected') for e in self.single_disconnect_event.values(): e(device=dev.device) else: dev.disconnect_count += 1 print(dev.alias, 'hwid:', dev.hw_id, 'id:', id(dev), 'dcnts:', dev.disconnect_count) new_device_set.add(dev) offline_devices = self.current_device_set - new_device_set for e in self.multi_disconnect_event.values(): e(device_list=[d.device for d in offline_devices]) new_devices = new_device_set - self.current_device_set #print('new_devices:'.ljust(18), (new_devices) or '-') for e in self.multi_connect_event.values(): e(device_list=[d.device for d in new_devices]) for dev in new_devices: print(dev, 'connected') for e in self.single_connect_event.values(): e(device=dev.device, is_on=dev.device.is_on) #print('noch:'.ljust(18), (new_device_set & self.current_device_set) or '-') self.current_device_set = new_device_set time.sleep(self.time_between_pings) class RegisterTrigger: def __init__(self, action, trigger_status, mult='single', timeout=1, time_between_pings=2, threshold=10): from uuid import uuid4 self.uuid = uuid4() self.mult = mult self.trigger_status = trigger_status d = Discoverer() if trigger_status == 'connect': if mult == 'single': d.single_connect_event[self.uuid] = action elif mult == 'multi': d.multi_connect_event[self.uuid] = action elif trigger_status == 'disconnect': if mult == 'single': d.single_disconnect_event[self.uuid] = action elif mult == 'multi': d.multi_disconnect_event[self.uuid] = action d.set_params(timeout=timeout, time_between_pings=time_between_pings, threshold=threshold) print('trigger registered') def unregister(self): d = Discoverer() if self.trigger_status == 'connect': if self.mult == 'single': del d.single_connect_event[self.uuid] elif self.mult == 'multi': del d.multi_connect_event[self.uuid] elif self.trigger_status == 'disconnect': if self.mult == 'single': del d.single_disconnect_event[self.uuid] elif self.mult == 'multi': del d.multi_disconnect_event[self.uuid] def set_device_property(device, brightness=None, temperature=None, is_on=None): if type(device) is str: d = Discoverer() for dev in [dv.device for dv in d.current_device_set if dv.alias == device]: set_device_property(dev, brightness, temperature, is_on) else: if is_on is not None: if is_on: device.turn_on() else: device.turn_off() if brightness is not None: device.brightness = brightness if temperature is not None: device.color_temp = temperature actions = {'set TP-Link device property': {'run': set_device_property, 'init': Discover}} triggers = {'TP-Link device': RegisterTrigger} if __name__ == "__main__": def a(*args, **kwargs): print('==>', *args, **kwargs) def b(*args, **kwargs): print('<==', *args, **kwargs) RegisterTrigger(a, 'connect') RegisterTrigger(b, 'disconnect') while True: time.sleep(10 * 60) PK!R00*eeve/eeve plugins/win_key_hook/__init__.pyimport ctypes import collections import threading import travel_backpack import win32con import win32api import win32gui from typing import Callable import atexit from win_key_hook.input_parser import InputParser from uuid import uuid4 class WinLowLevelHook(metaclass=travel_backpack.Singleton): def __init__(self, keyboard_callback, mouse_callback): self.keyboard_callback = keyboard_callback self.mouse_callback = mouse_callback self.KeyboardEvent = collections.namedtuple('KeyboardEvent', ['event_type', 'key_code', 'scan_code', 'alt_pressed', 'time', 'extra_info']) self.MouseEvent = collections.namedtuple('MouseEvent', ['event_type', 'point', 'wheel_direction', 'injection', 'time']) self.hook_id_keyboard = None self.hook_id_mouse = None def start(self, asynchronous=True): if asynchronous: print('async hook') threading.Thread(target=self.start, kwargs={'asynchronous': False}).start() return print('starting hook') keyboard_event_types = { win32con.WM_KEYDOWN: 'key down', win32con.WM_KEYUP: 'key up', 0x104: 'key down', # WM_SYSKEYDOWN, used for Alt key. 0x105: 'key up', # WM_SYSKEYUP, used for Alt key. } # WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP. mouse_event_types = { win32con.WM_RBUTTONDOWN: 'RMB down', win32con.WM_RBUTTONUP: 'RMB up', win32con.WM_LBUTTONDOWN: 'LMB down', win32con.WM_LBUTTONUP: 'LMB up', 0x0207: 'MMB down', 0x0208: 'MMB up', win32con.WM_MOUSEWHEEL: 'Wheel', 0x020E: 'Horizontal Wheel', # win32con.WM_MOUSEHWHEEL win32con.WM_MOUSEMOVE: 'mouse move' } def low_level_keyboard_handler(nCode, wParam, lParam): event = (nCode, self.KeyboardEvent( event_type=keyboard_event_types[wParam], key_code=lParam[0] & 0xFFFFFFFF, scan_code=lParam[1] & 0xFFFFFFFF, alt_pressed=lParam[2] == 32, time=lParam[3] & 0xFFFFFFFF if lParam[3] is not None else 0, extra_info=lParam[4])) # unsigned int 64 for 32bit # Be a good neighbor and call the next hook. if self.keyboard_callback(event): return ctypes.windll.user32.CallNextHookEx(self.hook_id_keyboard, nCode, wParam, lParam) else: print("#") return 1 def low_level_mouse_handler(nCode, wParam, lParam): event = (nCode, self.MouseEvent( event_type=mouse_event_types.get(wParam, f'unknown ({hex(wParam)})'), point=lParam[0], wheel_direction=lParam[1], injection=lParam[2], time=lParam[3])) if self.mouse_callback(event): # Be a good neighbor and call the next hook. return ctypes.windll.user32.CallNextHookEx(self.hook_id_mouse, nCode, wParam, lParam) else: return 1 CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)) # Convert the Python handler into C pointer. pointer_keyboard = CMPFUNC(low_level_keyboard_handler) pointer_mouse = CMPFUNC(low_level_mouse_handler) # Hook both key up and key down events for common keys (non-system). try: wh_keyboard_lo_level = win32con.WH_KEYBOARD_LL wh_mouse_low_level = win32con.WH_MOUSE_LL p_keyoard = pointer_keyboard p_mouse = pointer_mouse module_handle = win32api.GetModuleHandle(None) module_handle = 0 self.hook_id_keyboard = ctypes.windll.user32.SetWindowsHookExA(wh_keyboard_lo_level, p_keyoard, module_handle, 0) self.hook_id_mouse = ctypes.windll.user32.SetWindowsHookExA(wh_mouse_low_level, p_mouse, module_handle, 0) except Exception as ex: print(ex) # Register to remove the hook when the interpreter exits. # Unfortunately a # try/finally block doesn't seem to work here. atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.hook_id_keyboard) atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.hook_id_mouse) while True: #peek_result, msg = win32gui.PeekMessage(None, 0, 0, 1) result, msg = win32gui.GetMessage(None, 0, 0) print('got msg:', msg, result) win32gui.TranslateMessage(msg) #print('translated') win32gui.DispatchMessage(msg) #print('sent') #sleep(0.5) pass class BaseKeyInfo: def __init__(self, chain: str, func: Callable, allowed_tag: str): print(f'[{chain}]') chain += 'Z' self.chain = [] temp_w = None for c in chain: if not c.isupper(): temp_w += c else: if temp_w: self.chain.append(temp_w) temp_w = c self.func = func self.currentIndex = 0 self.allowed_tag = allowed_tag.lower() if allowed_tag is not None else None for c in self.chain: print('key in chain:', c) def can_run(self) -> bool: if self.allowed_tag is None: return True else: if not KeyHookWrapper().allowed_tags: return False else: if self.allowed_tag in KeyHookWrapper().allowed_tags or self.allowed_tag == 'any' or 'any' in KeyHookWrapper().allowed_tags: return True else: return False def next(self, char: str) -> bool: if self.can_run(): char = char.capitalize() return self._next(char) else: return False def _next(self, char: str) -> bool: raise NotImplementedError('This method must be implemented by child classes') class KeychainInfo(BaseKeyInfo): def _next(self, char: str) -> bool: if char == self.chain[self.currentIndex]: self.currentIndex += 1 elif char == self.chain[0]: self.currentIndex = 1 else: self.currentIndex = 0 # print('index', self.currentIndex) # print(char, self.currentIndex) if self.currentIndex == len(self.chain): self.currentIndex = 0 self.func() return True else: return False class KeyCombinationInfo(BaseKeyInfo): def _next(self, char): res = True kd = [k.lower() for k in KeyHookWrapper().keys_down] for c in self.chain: res = res and (c.lower() in kd) #print(res, c) if res: self.func() return res class KeyHookWrapper(metaclass=travel_backpack.Singleton): def __init__(self): print('initializing Key Hook Wrapper') self.keyboard_callbacks = dict() self.mouse_callbacks = dict() self.keychains = dict() self.allowed_tags = set() self.keys_down = set() def keyboard_callback(key_name): ret = True for cb in self.keyboard_callbacks.values(): cb(key_name=key_name) for kc, swallow_key in self.keychains.values(): if kc.next(char=key_name): if kc.allowed_tag is not None: self.allowed_tags = set() if swallow_key: ret = False return ret and not self.allowed_tags def mouse_hook_callback(info): nCode, event = info nCode # just to stop error on line above and keep the name if event.event_type == 'LMB down': for cb in self.mouse_callbacks.values(): cb(key_name=event.event_type, point=event.point, wheel_direction=event.wheel_direction, injection=event.injection, time=event.time) return True parser = InputParser(key_down_callback=keyboard_callback, key_up_callback=None, keys_down=self.keys_down) hook = WinLowLevelHook(parser.process_keyboard_event, mouse_hook_callback) print('Starting input hook') hook.start() def add_keyboard_callback(self, callback): uuid = uuid4() self.keyboard_callbacks[uuid] = callback return uuid def remove_keyboard_callback(self, uuid): del self.keyboard_callbacks[uuid] def add_mouse_callback(self, callback): uuid = uuid4() self.mouse_callbacks[uuid] = callback return uuid def remove_mouse_callback(self, uuid): del self.mouse_callbacks[uuid] def add_keychain_callback(self, callback, keychain: str, swallow_key: bool, only_on_blocking_mode: bool): uuid = uuid4() self.keychains[uuid] = KeychainInfo(chain=keychain, func=callback, allowed_tag=only_on_blocking_mode), swallow_key return uuid def remove_keychain_callback(self, uuid): del self.keychains[uuid] def add_keycombination_callback(self, callback, keys: str, swallow_key: bool, only_on_blocking_mode: bool): uuid = uuid4() self.keychains[uuid] = KeyCombinationInfo(chain=keys, func=callback, allowed_tag=only_on_blocking_mode), swallow_key return uuid def remove_keycombination_callback(self, uuid): del self.keychains[uuid] class RegisterKeyDown: def __init__(self, action): self.uuid = KeyHookWrapper().add_keyboard_callback(action) def unregister(self): KeyHookWrapper().remove_keyboard_callback(self.uuid) class RegisterMouseDown: def __init__(self, action): self.uuid = KeyHookWrapper().add_mouse_callback(action) def unregister(self): KeyHookWrapper().remove_mouse_callback(self.uuid) class RegisterKeychain: def __init__(self, action, keychain: str, swallow_key=False, only_on_blocking_mode=None): self.uuid = KeyHookWrapper().add_keychain_callback(action, keychain, swallow_key, only_on_blocking_mode) def unregister(self): KeyHookWrapper().remove_keychain_callback(self.uuid) class RegisterKeyCombination: def __init__(self, action, keys: str, swallow_last_key=False, only_on_blocking_mode=None): self.uuid = KeyHookWrapper().add_keycombination_callback(action, keys, swallow_last_key, only_on_blocking_mode) def unregister(self): KeyHookWrapper().remove_keychain_callback(self.uuid) class SetKeyBlockingMode: def run(self, value='any'): value = value.lower() kw = KeyHookWrapper() if value in kw.allowed_tags: print('Removing', value, 'from allowed tags') kw.allowed_tags.remove(value) else: print('Adding', value, 'to allowed tags') kw.allowed_tags.add(value) print('key blocking mode is', kw.allowed_tags) triggers = {'key down': RegisterKeyDown, 'mouse down': RegisterMouseDown, 'keychain': RegisterKeychain, 'keys down': RegisterKeyCombination} actions = {'toggle keychain mode': SetKeyBlockingMode} def main(): print('initializing Key Hook') def keyboard_callback(key_name): print(key_name) return True def mouse_hook_callback(info): nCode, event = info nCode # just to stop error on line above and keep the name event return True parser = InputParser(key_down_callback=keyboard_callback, key_up_callback=None) hook = WinLowLevelHook(parser.process_keyboard_event, mouse_hook_callback) print('Starting input hook') hook.start() if __name__ == '__main__': main() PK!Y%%*eeve/eeve plugins/win_key_hook/__main__.pyfrom win_key_hook import main main()PK!\ .eeve/eeve plugins/win_key_hook/input_parser.pyclass InputParser: def __init__(self, key_down_callback=None, key_up_callback=None, keys_down=None, new_key_down_callback=None): if keys_down is None: self.keys_down = set() else: self.keys_down = keys_down self.key_down_callback = key_down_callback self.key_up_callback = key_up_callback self.new_key_down_callback = new_key_down_callback def process_keyboard_event(self, event, verbose=False): # print(event) nCode, e = event key_code = e.key_code # DWORD = 32bit: ffff ffff key_code_special = 0 key_name = hex(e.key_code)[2:] key_code_special = int((e.key_code & 0xFF00000000) / 0x100000000) if verbose: print('type:', e.event_type) print(' key:', hex(e.key_code)) print('scan:', hex(e.scan_code)) print(' alt:', e.alt_pressed) print('\n\n') special = False import win_key_hook.key_map as key_map try: key_name = key_map.key_code_map[key_code] except KeyError: try: key_name = key_map.key_code_map[e.key_code] except KeyError: try: key_name = key_map.key_code_special_map[key_code_special] special = True except KeyError: pass if e.event_type == 'key down': return self.on_key_down(key_name, special) else: return self.on_key_up(key_name) def on_key_down(self, key_name, is_special_key): print(key_name, end='', flush=True) if key_name not in self.keys_down: self.keys_down.add(key_name) if self.new_key_down_callback is not None: self.new_key_down_callback(key_name) if self.key_down_callback is not None: return self.key_down_callback(key_name) return True def on_key_up(self, key_name): try: self.keys_down.remove(key_name) except KeyError as ke: print('\nkey error:', ke) except Exception as ex: import travel_backpack print(travel_backpack.format_exception_string(ex)) if self.key_up_callback is not None: return self.key_up_callback(key_name) return TruePK!)eEEKEK)eeve/eeve plugins/win_key_hook/key_map.pykey_map = {"Modifiers" : -65536, # # Summary: # No key pressed. "None" : 0, # # Summary: # The left mouse button. "LButton" : 1, # # Summary: # The right mouse button. "RButton" : 2, # # Summary: # The CANCEL key. "Cancel" : 3, # # Summary: # The middle mouse button (three-button mouse). "MButton" : 4, # # Summary: # The first x mouse button (five-button mouse). "XButton1" : 5, # # Summary: # The second x mouse button (five-button mouse). "XButton2" : 6, # # Summary: # The BACKSPACE key. "Back" : 8, # # Summary: # The TAB key. "Tab" : 9, # # Summary: # The LINEFEED key. "LineFeed" : 10, # # Summary: # The CLEAR key. "Clear" : 12, # # Summary: # The RETURN key. "Return" : 13, # # Summary: # The ENTER key. "Enter" : 13, # # Summary: # The SHIFT key. "ShiftKey" : 16, # # Summary: # The CTRL key. "ControlKey" : 17, # # Summary: # The ALT key. "Menu" : 18, # # Summary: # The PAUSE key. "Pause" : 19, # # Summary: # The CAPS LOCK key. "Capital" : 20, # # Summary: # The CAPS LOCK key. "CapsLock" : 20, # # Summary: # The IME Kana mode key. "KanaMode" : 21, # # Summary: # The IME Hanguel mode key. (maintained for compatibility; use HangulMode) "HanguelMode" : 21, # # Summary: # The IME Hangul mode key. "HangulMode" : 21, # # Summary: # The IME Junja mode key. "JunjaMode" : 23, # # Summary: # The IME final mode key. "FinalMode" : 24, # # Summary: # The IME Hanja mode key. "HanjaMode" : 25, # # Summary: # The IME Kanji mode key. "KanjiMode" : 25, # # Summary: # The ESC key. "Escape" : 27, # # Summary: # The IME convert key. "IMEConvert" : 28, # # Summary: # The IME nonconvert key. "IMENonconvert" : 29, # # Summary: # The IME accept key, replaces System.Windows.Forms.Keys.IMEAceept. "IMEAccept" : 30, # # Summary: # The IME accept key. Obsolete, use System.Windows.Forms.Keys.IMEAccept instead. "IMEAceept" : 30, # # Summary: # The IME mode change key. "IMEModeChange" : 31, # # Summary: # The SPACEBAR key. "Space" : 32, # # Summary: # The PAGE UP key. "Prior" : 33, # # Summary: # The PAGE UP key. "PageUp" : 33, # # Summary: # The PAGE DOWN key. "Next" : 34, # # Summary: # The PAGE DOWN key. "PageDown" : 34, # # Summary: # The END key. "End" : 35, # # Summary: # The HOME key. "Home" : 36, # # Summary: # The LEFT ARROW key. "Left" : 37, # # Summary: # The UP ARROW key. "Up" : 38, # # Summary: # The RIGHT ARROW key. "Right" : 39, # # Summary: # The DOWN ARROW key. "Down" : 40, # # Summary: # The SELECT key. "Select" : 41, # # Summary: # The PRINT key. "Print" : 42, # # Summary: # The EXECUTE key. "Execute" : 43, # # Summary: # The PRINT SCREEN key. "Snapshot" : 44, # # Summary: # The PRINT SCREEN key. "PrintScreen" : 44, # # Summary: # The INS key. "Insert" : 45, # # Summary: # The DEL key. "Delete" : 46, # # Summary: # The HELP key. "Help" : 47, # # Summary: # The 0 key. "D0" : 48, # # Summary: # The 1 key. "D1" : 49, # # Summary: # The 2 key. "D2" : 50, # # Summary: # The 3 key. "D3" : 51, # # Summary: # The 4 key. "D4" : 52, # # Summary: # The 5 key. "D5" : 53, # # Summary: # The 6 key. "D6" : 54, # # Summary: # The 7 key. "D7" : 55, # # Summary: # The 8 key. "D8" : 56, # # Summary: # The 9 key. "D9" : 57, # # Summary: # The A key. "A" : 65, # # Summary: # The B key. "B" : 66, # # Summary: # The C key. "C" : 67, # # Summary: # The D key. "D" : 68, # # Summary: # The E key. "E" : 69, # # Summary: # The F key. "F" : 70, # # Summary: # The G key. "G" : 71, # # Summary: # The H key. "H" : 72, # # Summary: # The I key. "I" : 73, # # Summary: # The J key. "J" : 74, # # Summary: # The K key. "K" : 75, # # Summary: # The L key. "L" : 76, # # Summary: # The M key. "M" : 77, # # Summary: # The N key. "N" : 78, # # Summary: # The O key. "O" : 79, # # Summary: # The P key. "P" : 80, # # Summary: # The Q key. "Q" : 81, # # Summary: # The R key. "R" : 82, # # Summary: # The S key. "S" : 83, # # Summary: # The T key. "T" : 84, # # Summary: # The U key. "U" : 85, # # Summary: # The V key. "V" : 86, # # Summary: # The W key. "W" : 87, # # Summary: # The X key. "X" : 88, # # Summary: # The Y key. "Y" : 89, # # Summary: # The Z key. "Z" : 90, # # Summary: # The left Windows logo key (Microsoft Natural Keyboard). "LWin" : 91, # # Summary: # The right Windows logo key (Microsoft Natural Keyboard). "RWin" : 92, # # Summary: # The application key (Microsoft Natural Keyboard). "Apps" : 93, # # Summary: # The computer sleep key. "Sleep" : 95, # # Summary: # The 0 key on the numeric keypad. "NumPad0" : 96, # # Summary: # The 1 key on the numeric keypad. "NumPad1" : 97, # # Summary: # The 2 key on the numeric keypad. "NumPad2" : 98, # # Summary: # The 3 key on the numeric keypad. "NumPad3" : 99, # # Summary: # The 4 key on the numeric keypad. "NumPad4" : 100, # # Summary: # The 5 key on the numeric keypad. "NumPad5" : 101, # # Summary: # The 6 key on the numeric keypad. "NumPad6" : 102, # # Summary: # The 7 key on the numeric keypad. "NumPad7" : 103, # # Summary: # The 8 key on the numeric keypad. "NumPad8" : 104, # # Summary: # The 9 key on the numeric keypad. "NumPad9" : 105, # # Summary: # The multiply key. "Multiply" : 106, # # Summary: # The add key. "Add" : 107, # # Summary: # The separator key. "Separator" : 108, # # Summary: # The subtract key. "Subtract" : 109, # # Summary: # The decimal key. "Decimal" : 110, # # Summary: # The divide key. "Divide" : 111, # # Summary: # The F1 key. "F1" : 112, # # Summary: # The F2 key. "F2" : 113, # # Summary: # The F3 key. "F3" : 114, # # Summary: # The F4 key. "F4" : 115, # # Summary: # The F5 key. "F5" : 116, # # Summary: # The F6 key. "F6" : 117, # # Summary: # The F7 key. "F7" : 118, # # Summary: # The F8 key. "F8" : 119, # # Summary: # The F9 key. "F9" : 120, # # Summary: # The F10 key. "F10" : 121, # # Summary: # The F11 key. "F11" : 122, # # Summary: # The F12 key. "F12" : 123, # # Summary: # The F13 key. "F13" : 124, # # Summary: # The F14 key. "F14" : 125, # # Summary: # The F15 key. "F15" : 126, # # Summary: # The F16 key. "F16" : 127, # # Summary: # The F17 key. "F17" : 128, # # Summary: # The F18 key. "F18" : 129, # # Summary: # The F19 key. "F19" : 130, # # Summary: # The F20 key. "F20" : 131, # # Summary: # The F21 key. "F21" : 132, # # Summary: # The F22 key. "F22" : 133, # # Summary: # The F23 key. "F23" : 134, # # Summary: # The F24 key. "F24" : 135, # # Summary: # The NUM LOCK key. "NumLock" : 144, # # Summary: # The SCROLL LOCK key. "Scroll" : 145, # # Summary: # The left SHIFT key. "LShiftKey" : 160, # # Summary: # The right SHIFT key. "RShiftKey" : 161, # # Summary: # The left CTRL key. "LControlKey" : 162, # # Summary: # The right CTRL key. "RControlKey" : 163, # # Summary: # The left ALT key. "LMenu" : 164, # # Summary: # The right ALT key. "RMenu" : 165, # # Summary: # The browser back key (Windows 2000 or later). "BrowserBack" : 166, # # Summary: # The browser forward key (Windows 2000 or later). "BrowserForward" : 167, # # Summary: # The browser refresh key (Windows 2000 or later). "BrowserRefresh" : 168, # # Summary: # The browser stop key (Windows 2000 or later). "BrowserStop" : 169, # # Summary: # The browser search key (Windows 2000 or later). "BrowserSearch" : 170, # # Summary: # The browser favorites key (Windows 2000 or later). "BrowserFavorites" : 171, # # Summary: # The browser home key (Windows 2000 or later). "BrowserHome" : 172, # # Summary: # The volume mute key (Windows 2000 or later). "VolumeMute" : 173, # # Summary: # The volume down key (Windows 2000 or later). "VolumeDown" : 174, # # Summary: # The volume up key (Windows 2000 or later). "VolumeUp" : 175, # # Summary: # The media next track key (Windows 2000 or later). "MediaNextTrack" : 176, # # Summary: # The media previous track key (Windows 2000 or later). "MediaPreviousTrack" : 177, # # Summary: # The media Stop key (Windows 2000 or later). "MediaStop" : 178, # # Summary: # The media play pause key (Windows 2000 or later). "MediaPlayPause" : 179, # # Summary: # The launch mail key (Windows 2000 or later). "LaunchMail" : 180, # # Summary: # The select media key (Windows 2000 or later). "SelectMedia" : 181, # # Summary: # The start application one key (Windows 2000 or later). "LaunchApplication1" : 182, # # Summary: # The start application two key (Windows 2000 or later). "LaunchApplication2" : 183, # # Summary: # The OEM Semicolon key on a US standard keyboard (Windows 2000 or later). "OemSemicolon" : 186, # # Summary: # The OEM 1 key. "Oem1" : 186, # # Summary: # The OEM plus key on any country/region keyboard (Windows 2000 or later). "Oemplus" : 187, # # Summary: # The OEM comma key on any country/region keyboard (Windows 2000 or later). "Oemcomma" : 188, # # Summary: # The OEM minus key on any country/region keyboard (Windows 2000 or later). "OemMinus" : 189, # # Summary: # The OEM period key on any country/region keyboard (Windows 2000 or later). "OemPeriod" : 190, # # Summary: # The OEM question mark key on a US standard keyboard (Windows 2000 or later). "OemQuestion" : 191, # # Summary: # The OEM 2 key. "Oem2" : 191, # # Summary: # The OEM tilde key on a US standard keyboard (Windows 2000 or later). "Oemtilde" : 192, # # Summary: # The OEM 3 key. "Oem3" : 192, # # Summary: # The OEM open bracket key on a US standard keyboard (Windows 2000 or later). "OemOpenBrackets" : 219, # # Summary: # The OEM 4 key. "Oem4" : 219, # # Summary: # The OEM pipe key on a US standard keyboard (Windows 2000 or later). "OemPipe" : 220, # # Summary: # The OEM 5 key. "Oem5" : 220, # # Summary: # The OEM close bracket key on a US standard keyboard (Windows 2000 or later). "OemCloseBrackets" : 221, # # Summary: # The OEM 6 key. "Oem6" : 221, # # Summary: # The OEM singled/double quote key on a US standard keyboard (Windows 2000 or later). "OemQuotes" : 222, # # Summary: # The OEM 7 key. "Oem7" : 222, # # Summary: # The OEM 8 key. "Oem8" : 223, # # Summary: # The OEM angle bracket or backslash key on the RT 102 key keyboard (Windows 2000 # or later). "OemBackslash" : 226, # # Summary: # The OEM 102 key. "Oem102" : 226, # # Summary: # The PROCESS KEY key. "ProcessKey" : 229, # # Summary: # Used to pass Unicode characters as if they were keystrokes. The Packet key value # is the low word of a 32-bit virtual-key value used for non-keyboard input methods. "Packet" : 231, # # Summary: # The ATTN key. "Attn" : 246, # # Summary: # The CRSEL key. "Crsel" : 247, # # Summary: # The EXSEL key. "Exsel" : 248, # # Summary: # The ERASE EOF key. "EraseEof" : 249, # # Summary: # The PLAY key. "Play" : 250, # # Summary: # The ZOOM key. "Zoom" : 251, # # Summary: # A constant reserved for future use. "NoName" : 252, # # Summary: # The PA1 key. "Pa1" : 253, # # Summary: # The CLEAR key. "OemClear" : 254, # # Summary: # The bitmask to extract a key code from a key value. "KeyCode" : 65535, # # Summary: # The SHIFT modifier key. "Shift" : 65536, # # Summary: # The CTRL modifier key. "Control" : 131072, # # Summary: # The ALT modifier key. "Alt" : 262144} key_map_special = { # # Summary: # The fn Brightness+ key. "BrightnessUp" : 8, # # Summary: # The fn Brightness- key. "BrightnessDown" : 9, # # Summary: # The fn Fan key. "Fan" : 51, # # Summary: # The fn Settings key. "Settings" : 78, # # Summary: # The fn WiFi key. "Wifi" : 85} key_code_map = {v : k for k, v in key_map.items()} key_code_special_map = {v : k for k, v in key_map_special.items()}PK!}Ũ..&eeve/eeve triggers/broadcastCatcher.pyfrom travel_backpack import log_info as li import win32con import win32api import win32gui import time from ctypes import POINTER, windll, Structure, cast, CFUNCTYPE, c_int, c_uint, c_void_p, c_bool, sizeof, Union, c_ushort, c_ulong, c_long, c_char from comtypes import GUID from ctypes.wintypes import HANDLE, DWORD, USHORT, HWND, WPARAM, ULONG, LONG, UINT, BYTE from datetime import datetime as dt now = dt.now log_info = lambda info: li(info, file='System.log', print_time=False) PBT_POWERSETTINGCHANGE = 0x8013 WM_INPUT = 0x00FF display_on_callbacks = dict() display_off_callbacks = dict() display_dim_callbacks = dict() session_end_callbacks = dict() sys_suspend_callbacks = dict() def display_change(data): mapping = {0: 'off', 1: 'on', 2: 'dimmed'} status = mapping[data] if status == 'on': for c in display_on_callbacks.values(): #print('fon') c() elif status == 'off': for c in display_off_callbacks.values(): #print('foff') c() elif status == 'dimmed': for c in display_dim_callbacks.values(): #print('fdim') c() return "Display " + status power_settings = { 'SYSTEM_AWAYMODE': { 'GUID': '{98A7F580-01F7-48AA-9C0F-44352C29E5C0}', 'Data': { 0: 'Exiting away mode', 1: 'Entering away mode' } }, 'CONSOLE_DISPLAY_STATE': { 'GUID': '{6FE69556-704A-47A0-8F24-C28D936FDA47}', 'Data': display_change }, 'ACDC_POWER_SOURCE': { 'GUID': '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}', 'Data': { 0: 'AC power', 1: 'DC power (batteries)', 2: 'Short term power source (UPS)' } }, 'BATTERY_PERCENTAGE_REMAINING': { 'GUID': '{A7AD8041-B45A-4CAE-87A3-EECBB468A9E1}', 'Data': lambda batt: f'Battery remaining: {batt}' } } GUID_to_name = {v['GUID']: k for k, v in power_settings.items()} def process_power_broadcast(lparam): settings = cast(lparam, POINTER(POWERBROADCAST_SETTING)).contents power_setting_GUID = str(settings.PowerSetting) data_length = settings.DataLength data = settings.Data data_info = power_settings.get(GUID_to_name.get(power_setting_GUID)).get('Data') if data_info is None: log_info(f'Power setting changed but GUID is unknown: {power_setting_GUID}') elif callable(data_info): log_info(data_info(data)) else: log_info(data_info.get(data, f'Unknown Data: {hex(data)}({data})')) def system_suspend(x): for c in sys_suspend_callbacks.values(): log_info('f sys suspend') c() log_info('System suspend') power_broadcast_wParams = { win32con.PBT_APMPOWERSTATUSCHANGE: lambda x: log_info('Power status has changed'), win32con.PBT_APMRESUMEAUTOMATIC: lambda x: log_info('System resume'), win32con.PBT_APMRESUMESUSPEND: lambda x: log_info('System resume by user input'), win32con.PBT_APMSUSPEND: system_suspend, PBT_POWERSETTINGCHANGE: process_power_broadcast } end_session_lParams = { 0x00000000: 'Shutdown or Restart', 0x00000001: 'Using file that must be replaced, system being serviced or resourses exausted', 0x40000000: 'Force shutdown', 0x80000000: 'User logging off' } class POWERBROADCAST_SETTING(Structure): _fields_ = [("PowerSetting", GUID), ("DataLength", DWORD), ("Data", DWORD)] class RECT(Structure): _fields_ = [('left', c_long), ('top', c_long), ('right', c_long), ('bottom', c_long)] class PAINTSTRUCT(Structure): _fields_ = [('hdc', c_int), ('fErase', c_int), ('rcPaint', RECT), ('fRestore', c_int), ('fIncUpdate', c_int), ('rgbReserved', c_char * 32)] class POINT(Structure): _fields_ = [('x', c_long), ('y', c_long)] class MSG(Structure): _fields_ = [('hwnd', c_int), ('message', c_uint), ('wParam', c_int), ('lParam', c_int), ('time', c_int), ('pt', POINT)] class RAWINPUTDEVICE(Structure): _fields_ = [ ("usUsagePage", c_ushort), ("usUsage", c_ushort), ("dwFlags", DWORD), ("hwndTarget", HWND), ] class RAWINPUTHEADER(Structure): _fields_ = [ ("dwType", DWORD), ("dwSize", DWORD), ("hDevice", HANDLE), ("wParam", WPARAM), ] class RAWMOUSE(Structure): class _U1(Union): class _S2(Structure): _fields_ = [ ("usButtonFlags", c_ushort), ("usButtonData", c_ushort), ] _fields_ = [ ("ulButtons", ULONG), ("_s2", _S2), ] _fields_ = [ ("usFlags", c_ushort), ("_u1", _U1), ("ulRawButtons", ULONG), ("lLastX", LONG), ("lLastY", LONG), ("ulExtraInformation", ULONG), ] _anonymous_ = ("_u1", ) class RAWKEYBOARD(Structure): _fields_ = [ ("MakeCode", c_ushort), ("Flags", c_ushort), ("Reserved", c_ushort), ("VKey", c_ushort), ("Message", UINT), ("ExtraInformation", ULONG), ] class RAWHID(Structure): _fields_ = [ ("dwSizeHid", DWORD), ("dwCount", DWORD), ("bRawData", BYTE), ] class RAWINPUT(Structure): class _U1(Union): _fields_ = [ ("mouse", RAWMOUSE), ("keyboard", RAWKEYBOARD), ("hid", RAWHID), ] _fields_ = [ ("header", RAWINPUTHEADER), ("_u1", _U1), ("hDevice", HANDLE), ("wParam", WPARAM), ] _anonymous_ = ("_u1", ) def wndproc(hwnd, msg, wparam, lparam): print(f"wndproc: {msg} w: {hex(wparam)} l: {hex(lparam)}") if msg == win32con.WM_POWERBROADCAST: power_broadcast_wParams.get(wparam, lambda x: log_info(f'Unknown wParam for WM_POWERBROADCAST ({wparam})'))(lparam) return True elif msg == win32con.WM_QUERYENDSESSION: log_info("Query End Session") for k, v in end_session_lParams.items(): if lparam & k: log_info(v) for c in session_end_callbacks.values(): #print('fsend') log_info('Executing eeve') c() log_info('Executed eeve') #return False # to cancel shutdown return True elif msg == win32con.WM_ENDSESSION: log_info("End Session") for k, v in end_session_lParams.items(): if lparam & k: log_info(v) return 0 elif msg == WM_INPUT: process_input(wparam, lparam) else: log_info(f"wndproc: {msg}\nw: {hex(wparam)}\nl: {hex(lparam)}") running = False GetRawInputData = windll.user32.GetRawInputData def process_input(wParam, hRawInput): print('input from background?:', wParam) RID_INPUT = 0x10000003 POINTER(c_int) ri = RAWINPUT() GetRawInputData(hRawInput, RID_INPUT, POINTER(ri), POINTER(c_int), sizeof(RAWINPUTHEADER)) print('type:', ri.header.dwType) print('device:', ri.header.hDevice) print('message:', ri.data.keyboard.Message) print('vKey:', ri.data.keyboard.VKey) print('scan code:', ri.data.keyboard.MakeCode) print('state:', ri.data.keyboard.Flags) pass def run(): global running if running: return running = True log_info("*** STARTING ***") running = True hinst = win32api.GetModuleHandle(None) wndclass = win32gui.WNDCLASS() wndclass.hInstance = hinst wndclass.lpszClassName = "PyWindow" CMPFUNC = CFUNCTYPE(c_bool, c_int, c_uint, c_uint, c_void_p) wndproc_pointer = CMPFUNC(wndproc) messageMap = { win32con.WM_POWERBROADCAST: wndproc_pointer, win32con.WM_QUERYENDSESSION: wndproc, win32con.WM_ENDSESSION: wndproc, win32con.WM_SYSCOMMAND: wndproc, win32con.WM_DESTROY: wndproc, win32con.WM_CLOSE: wndproc, win32con.WM_QUIT: wndproc, WM_INPUT: wndproc } try: myWindowClass = win32gui.RegisterClass(wndclass) #hwnd = win32gui.CreateWindowEx(win32con.WS_EX_LEFT, myWindowClass, "PyWindow", 0, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, # hinst, None) hwnd = win32gui.CreateWindowEx(win32con.WS_EX_LEFT, wndclass.lpszClassName, "PyWindow", 0, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, win32con.NULL, win32con.NULL, hinst, None) except Exception as e: log_info("Exception: %s" % str(e)) if hwnd is None: log_info("hwnd is none!") else: log_info("hwnd: %s" % hwnd) ''' rid = RAWINPUTDEVICE() rid.usUsagePage = 1 rid.usUsage = 6 rid.dwFlags = 0x00000100 # RIDEV_INPUTSINK rid.hwndTarget = HWND(hwnd) ridptr = POINTER(RAWINPUTDEVICE)(rid) ridsz = sizeof(rid) RegisterRawInputDevices = windll.user32.RegisterRawInputDevices print('register return:', RegisterRawInputDevices(ridptr, 1, ridsz)) GetRegisteredRawInputDevices = windll.user32.GetRegisteredRawInputDevices num_ptr = POINTER(UINT)(c_uint(5)) devs_ptr = POINTER(RAWINPUTDEVICE)(rid) print('registered devices:', GetRegisteredRawInputDevices(devs_ptr, num_ptr, ridsz)) print(num_ptr.contents) ''' # Register for raw input Rid = (1 * RAWINPUTDEVICE)() Rid[0].usUsagePage = 0x01 #Rid[0].usUsage = 0x02 Rid[0].usUsage = 6 RIDEV_INPUTSINK = 0x00000100 # Get events even when not focused Rid[0].dwFlags = RIDEV_INPUTSINK Rid[0].hwndTarget = hwnd RegisterRawInputDevices = windll.user32.RegisterRawInputDevices RegisterRawInputDevices(Rid, 1, sizeof(RAWINPUTDEVICE)) wndclass.lpfnWndProc = messageMap register_function = windll.user32.RegisterPowerSettingNotification hwnd_pointer = HANDLE(hwnd) for name, power_setting_info in power_settings.items(): result = register_function(hwnd_pointer, GUID(power_setting_info['GUID']), DWORD(0)) print('registering', name) #print('result:', hex(result)) #print('lastError:', win32api.GetLastError()) #print() #print('\nEntering loop') while True: win32gui.PumpWaitingMessages() time.sleep(1) #print('.', end='', flush=True) if __name__ == "__main__": run() def thread_run(): import travel_backpack travel_backpack.threadpool(run)() return global running if not running: running = True import travel_backpack travel_backpack.threadpool(run)() class Display: def __init__(self, action, status): from uuid import uuid4 self.uuid = uuid4() self.mappings = {'on': display_on_callbacks, 'off': display_off_callbacks, 'dimmed': display_dim_callbacks} self.status = status #print(action) self.mappings[self.status][self.uuid] = action thread_run() def unregister(self): del self.mappings[self.status][self.uuid] class SessionEnd: def __init__(self, action): from uuid import uuid4 self.uuid = uuid4() session_end_callbacks[self.uuid] = action thread_run() def unregister(self): del session_end_callbacks[self.uuid] class SystemSuspend: def __init__(self, action): from uuid import uuid4 self.uuid = uuid4() sys_suspend_callbacks[self.uuid] = action thread_run() def unregister(self): del sys_suspend_callbacks[self.uuid] triggers = {'display': Display, 'session end': SessionEnd, 'system suspend': SystemSuspend} PK!~eeve/eeve triggers/rawinput.pyimport structs import ctypes import time import win32con import win32gui import win32api def main(): register_raw = False if register_raw: WM_INPUT = 0xff RIDEV_INPUTSINK = 0x00000100 RID_INPUT = 0x10000003 def ErrorIfZero(handle): if handle == 0: raise Exception("WinError: handle is 0") else: return handle def winproc(hwnd, message, wParam, lParam): if message == WM_INPUT: GetRawInputData = ctypes.windll.user32.GetRawInputData NULL = ctypes.c_int(win32con.NULL) dwSize = ctypes.c_uint() GetRawInputData(lParam, RID_INPUT, NULL, ctypes.byref(dwSize), ctypes.sizeof(structs.RAWINPUTHEADER)) if dwSize.value == 40: raw = structs.RAWINPUT() if GetRawInputData(lParam, RID_INPUT, ctypes.byref(raw), ctypes.byref(dwSize), ctypes.sizeof(structs.RAWINPUTHEADER)) == dwSize.value: RIM_TYPEMOUSE = 0x00000000 RIM_TYPEKEYBOARD = 0x00000001 if raw.header.dwType == RIM_TYPEMOUSE: print((raw.header.hDevice, raw.mouse.usFlags, raw.mouse.ulButtons, raw.mouse._u1._s2.usButtonFlags, raw.mouse._u1._s2.usButtonData, raw.mouse.ulRawButtons, raw.mouse.lLastX, raw.mouse.lLastY, raw.mouse.ulExtraInformation)) elif raw.header.dwType == RIM_TYPEKEYBOARD: print() print('type:', raw.header.dwType) print('device:', raw.header.hDevice) print('message:', raw.keyboard.Message) print('vKey:', raw.keyboard.VKey) print('scan code:', raw.keyboard.MakeCode) print('state:', raw.keyboard.Flags) else: print("unknown dwType:", raw.header.dwType) else: print('different dwSize:', dwSize.value) else: print(f"wndproc: {message} w: {hex(wParam)} l: {hex(lParam)}") return ctypes.windll.user32.DefWindowProcA(ctypes.c_int(hwnd), ctypes.c_int(message), ctypes.c_int(wParam), ctypes.c_int(lParam)) # Define Window Class wndclass = win32gui.WNDCLASS() wndclass.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW wndclass.lpfnWndProc = structs.WNDPROC(winproc) #wndclass.cbClsExtra = 0 wndclass.cbWndExtra = 0 wndclass.hInstance = ctypes.windll.kernel32.GetModuleHandleA(ctypes.c_int(win32con.NULL)) wndclass.hIcon = ctypes.windll.user32.LoadIconA(ctypes.c_int(win32con.NULL), ctypes.c_int(win32con.IDI_APPLICATION)) wndclass.hCursor = ctypes.windll.user32.LoadCursorA(ctypes.c_int(win32con.NULL), ctypes.c_int(win32con.IDC_ARROW)) wndclass.hbrBackground = ctypes.windll.gdi32.GetStockObject(ctypes.c_int(win32con.WHITE_BRUSH)) #wndclass.lpszMenuName = None wndclass.lpszClassName = "MainWin" # Register Window Class #if not ctypes.windll.user32.RegisterClassA(ctypes.byref(wndclass)): if not win32gui.RegisterClass(wndclass): import win32api raise Exception("WinError:", win32api.GetLastError()) # Create Window HWND_MESSAGE = -3 hwnd = win32gui.CreateWindowEx( 0, wndclass.lpszClassName, "Python Window", 0, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, #HWND_MESSAGE, win32con.NULL, win32con.NULL, wndclass.hInstance, None) # Show Window #windll.user32.ShowWindow(c_int(hwnd), c_int(win32con.SW_SHOWNORMAL)) #windll.user32.UpdateWindow(c_int(hwnd)) # Register for raw input Rid = (1 * structs.RAWINPUTDEVICE)() Rid[0].usUsagePage = 0x01 Rid[0].usUsage = 0x06 Rid[0].dwFlags = RIDEV_INPUTSINK # Get events even when not focused Rid[0].hwndTarget = hwnd RegisterRawInputDevices = ctypes.windll.user32.RegisterRawInputDevices RegisterRawInputDevices(Rid, 1, ctypes.sizeof(structs.RAWINPUTDEVICE)) # register high-level keyboard hook (as low-level is executed before rawinput) hook_id_keyboard = None def high_level_keyboard_handler(nCode, wParam, lParam): print('YEAHHRR! BRUUUHH') return ctypes.windll.user32.CallNextHookEx(hook_id_keyboard, nCode, wParam, lParam) def init_get_bits(value: int): def get_bits(begin: int, end: int = None) -> int: if end is None: end = begin return (value >> begin) & (2**(end - begin + 1) - 1) return get_bits allow_passing = True print('action:', wParam) get_bits = init_get_bits(lParam) key_info = { 'repeat_count': get_bits(0, 15), 'scan_code': get_bits(16, 23), 'is_extended_key': get_bits(24), 'reserved_bits': get_bits(25, 28), 'is_alt_pressed': get_bits(29), 'was_down_before': get_bits(30), 'being_released': get_bits(31), } print('\n'.join([f'{k}: {v}' for k, v in key_info.items()])) if allow_passing: return ctypes.windll.user32.CallNextHookEx(hook_id_keyboard, nCode, wParam, lParam) else: print("#") return 1 CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)) p_keyboard = CMPFUNC(high_level_keyboard_handler) module_handle = win32api.GetModuleHandle(None) print(hex(module_handle)) module_handle = ctypes.wintypes.HMODULE(module_handle) print(module_handle) hook_id_keyboard = ctypes.windll.user32.SetWindowsHookExA(win32con.WH_KEYBOARD, p_keyboard, module_handle, 0) if not hook_id_keyboard: err = win32api.GetLastError() if err == 126: print('module not found') elif err == 1428: print('Cannot set nonlocal hook without a module handle.') else: print('Last Error:', err) while True: #peek_result, msg = win32gui.PeekMessage(None, 0, 0, 1) result, msg = win32gui.GetMessage(None, 0, 0) print('got msg:', msg, result) win32gui.TranslateMessage(msg) #print('translated') win32gui.DispatchMessage(msg) #print('sent') #sleep(0.5) pass while True: win32gui.PumpWaitingMessages() time.sleep(0.2) if __name__ == '__main__': main()PK!gfs eeve/eeve triggers/structs.pyfrom ctypes import POINTER, windll, Structure, cast, CFUNCTYPE, c_int, c_uint, c_void_p, c_bool, sizeof, Union, c_ushort, c_ulong, c_long, c_char, c_char_p, WINFUNCTYPE from comtypes import GUID from ctypes.wintypes import HANDLE, DWORD, USHORT, HWND, WPARAM, ULONG, LONG, UINT, BYTE WNDPROC = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int) argtypeList = [c_int, c_char_p, c_char_p, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int] class WNDCLASS(Structure): _fields_ = [('style', c_uint), ('lpfnWndProc', WNDPROC), ('cbClsExtra', c_int), ('cbWndExtra', c_int), ('hInstance', c_int), ('hIcon', c_int), ('hCursor', c_int), ('hbrBackground', c_int), ('lpszMenuName', c_char_p), ('lpszClassName', c_char_p)] class RECT(Structure): _fields_ = [('left', c_long), ('top', c_long), ('right', c_long), ('bottom', c_long)] class PAINTSTRUCT(Structure): _fields_ = [('hdc', c_int), ('fErase', c_int), ('rcPaint', RECT), ('fRestore', c_int), ('fIncUpdate', c_int), ('rgbReserved', c_char * 32)] class POINT(Structure): _fields_ = [('x', c_long), ('y', c_long)] class MSG(Structure): _fields_ = [('hwnd', c_int), ('message', c_uint), ('wParam', c_int), ('lParam', c_int), ('time', c_int), ('pt', POINT)] class RAWINPUTDEVICE(Structure): _fields_ = [ ("usUsagePage", c_ushort), ("usUsage", c_ushort), ("dwFlags", DWORD), ("hwndTarget", HWND), ] class RAWINPUTHEADER(Structure): _fields_ = [ ("dwType", DWORD), ("dwSize", DWORD), ("hDevice", HANDLE), ("wParam", WPARAM), ] class RAWMOUSE(Structure): class _U1(Union): class _S2(Structure): _fields_ = [ ("usButtonFlags", c_ushort), ("usButtonData", c_ushort), ] _fields_ = [ ("ulButtons", ULONG), ("_s2", _S2), ] _fields_ = [ ("usFlags", c_ushort), ("_u1", _U1), ("ulRawButtons", ULONG), ("lLastX", LONG), ("lLastY", LONG), ("ulExtraInformation", ULONG), ] _anonymous_ = ("_u1", ) class RAWKEYBOARD(Structure): _fields_ = [ ("MakeCode", c_ushort), ("Flags", c_ushort), ("Reserved", c_ushort), ("VKey", c_ushort), ("Message", UINT), ("ExtraInformation", ULONG), ] class RAWHID(Structure): _fields_ = [ ("dwSizeHid", DWORD), ("dwCount", DWORD), ("bRawData", BYTE), ] class RAWINPUT(Structure): class _U1(Union): _fields_ = [ ("mouse", RAWMOUSE), ("keyboard", RAWKEYBOARD), ("hid", RAWHID), ] _fields_ = [ ("header", RAWINPUTHEADER), ("_u1", _U1), ("hDevice", HANDLE), ("wParam", WPARAM), ] _anonymous_ = ("_u1", ) PK!0ugg#eeve/eeve triggers/timer_trigger.py_DEFAULT_POOL = None def threadpool(f, executor=None): global _DEFAULT_POOL from functools import wraps import concurrent.futures if _DEFAULT_POOL is None: _DEFAULT_POOL = concurrent.futures.ThreadPoolExecutor() @wraps(f) def wrap(*args, **kwargs): return (executor or _DEFAULT_POOL).submit(f, *args, **kwargs) return wrap class TimerTrigger: def __init__(self, action, t, start=False, max=0, **kwargs): #print('Loadinnnnnnn') self.action = action self.start = start self.run(int(t)) self.max_i = max #print('loadeded') def unregister(self): self.action = None @threadpool def run(self, t): import time i = 0 while self.action and (i < self.max_i or self.max_i == 0): if not self.start: self.start = True else: print('triggering action') self.action(time=t, trigger_count=i + 1) time.sleep(t) i += 1 triggers = {'timer': TimerTrigger}PK!>> eeve/eeve.pyif __name__ == "__main__": from . import main main()PK!Eeeve/helpers.pydef strip_split(s, *args, **kwargs): return list(map(lambda x: x.strip(), s.split(*args, **kwargs))) def try_cast(tp, obj): try: tp(obj) return True except: return False def get_true_value(x): if try_cast(int, x): return int(x) elif try_cast(float, x): return float(x) elif x.lower() == 'true': return True elif x.lower() == 'false': return False else: return x def split_args(arg_string, return_args, return_kwargs): args = strip_split(arg_string, mappings.char_map[',']) for arg in args: if mappings.char_map['='] in arg: k, v = strip_split(arg, mappings.char_map['='], maxsplit=1) return_kwargs[k] = get_true_value(v) else: return_args.append(get_true_value(arg)) import eeve.mappings as mappings def process_args(command_string, return_init_args): init_args, run_args = [], [] init_kwargs, run_kwargs = {}, {} if mappings.char_map[':'] in command_string: command_name, temp_args = strip_split(command_string, mappings.char_map[':'], maxsplit=1) arg_split = strip_split(temp_args, mappings.char_map['|'], maxsplit=1) if len(arg_split) == 1: temp_init_args = None temp_run_args = arg_split[0] else: temp_init_args, temp_run_args = arg_split if temp_init_args: split_args(temp_init_args, init_args, init_kwargs) split_args(temp_run_args, run_args, run_kwargs) else: command_name = command_string if return_init_args: return command_name, init_args, init_kwargs, run_args, run_kwargs else: return command_name, run_args, run_kwargsPK!neeve/importer.pyimport os import sys import importlib import travel_backpack def import_from_folder(folder): imported_files = [] folder = os.path.abspath(folder) if folder not in sys.path: sys.path.insert(0, folder) for file_obj in os.listdir(folder): try: if file_obj not in ['.', '..', '__pycache__', '.vscode']: print('inspecting', file_obj) file_obj = os.path.join(folder, file_obj) if os.path.isfile(file_obj): module_name = os.path.splitext(os.path.basename(file_obj))[0] #module_name = os.path.basename(file_obj) print('==> importing', module_name) imported_files.append(importlib.import_module(module_name)) elif os.path.isdir(file_obj): module_name = os.path.basename(file_obj) print('--> importing', module_name) imported_files.append(importlib.import_module(module_name)) except Exception as ex: print(travel_backpack.format_exception_string(ex)) return imported_files PK!$7eeve/mappings.py#import uuid import re #u = lambda: str(uuid.uuid4()).replace('-', '') special_chars = [':', '->', '=', ',', ';', '|'] weird_chars = 'ᨀᨁᨂᨃᨄᨅᨆᨇᨈ' char_map = {c: weird_chars[i] for i, c in enumerate(special_chars)} slash = 'Ø' def remap(e): e = replace_slashes(e) for char in char_map: #print('searching ', char) def sub(pat): #print('found:', '[', pat[0], ']') return pat[0][0] + char_map[char] c = char.replace('|', r'\|').replace('$', r'\$') pattern = fr"[^({slash})]{c}" e = re.sub(pattern, sub, e) #print(e) #print() return e.replace(slash, '') def replace_slashes(s): replaces = [] mx = len(s) i = 0 while i < mx: if s[i] == '\\': replaces.append(i) i += 1 i += 1 for r in reversed(replaces): s = s[:r] + slash + s[r + 1:] return s # test if __name__ == '__main__': char_map = {k: f'<[{k}]>' for k in char_map} slash = '#' s = r'timer: 2 -> test action: oi\, tudo bem? \=\,\\ Eu \= vc , init_par3= teste teste | trigger_result=$return_result' replaces = [] mx = len(s) i = 0 while i < mx: if s[i] == '\\': replaces.append(i) i += 1 i += 1 for r in reversed(replaces): s = s[:r] + slash + s[r + 1:] print(s) s = remap(s).replace(slash, '') print(s) print('end')PK!Pa eeve/taskinfo.pyfrom eeve.variable import VariableGroup from dataclasses import dataclass from typing import List @dataclass class TaskInfo: actions: 'List[Action]' global_variables: VariableGroup task_variables: VariableGroup local_variables: VariableGroup current_action_index: int = 0 increment_action_index: bool = True def get_next_actions(self): return enumerate(self.actions[self.current_action_index + 1:], self.current_action_index + 1) def get_var(self, var_name: str): original_var_name = var_name is_var = var_name.startswith('var$') class Scopes: Global = self.global_variables Task = self.task_variables Local = self.local_variables scope = Scopes.Local # remove 'var' prefix if is variable reference if is_var: var_name = var_name[3:] # define scope and remove all prefixing '$', being variable reference or just variable value if var_name.startswith('$$$'): scope = Scopes.Global var_name = var_name[3:] elif var_name.startswith('$$'): scope = Scopes.Task var_name = var_name[2:] elif var_name.startswith('$'): scope = Scopes.Local var_name = var_name[1:] # return variable ref or value if is_var: if var_name == 'vars': return scope.vars else: return scope.get_var(var_name) else: if var_name == 'vars': return scope.to_kwargs() else: return scope.get(var_name, original_var_name) def has_var(self, var_name: str) -> bool: is_var = var_name.startswith('var$') class Scopes: Global = self.global_variables Task = self.task_variables Local = self.local_variables scope = Scopes.Local if is_var: var_name = var_name[3:] if var_name.startswith('$$$'): scope = Scopes.Global var_name = var_name[3:] elif var_name.startswith('$$'): scope = Scopes.Task var_name = var_name[2:] elif var_name.startswith('$'): scope = Scopes.Local var_name = var_name[1:] return var_name in scope.vars PK!:eeve/variable.pyclass Variable: def __init__(self, name: str, value): self.value = value self._name = name @property def name(self): return self._name class VariableGroup: def __init__(self): self.vars = dict() def __getitem__(self, key): return self.vars[key].value def __setitem__(self, key, value): if type(value) is Variable: self.vars[key] = value else: self.vars[key] = Variable(name=key, value=value) def update(self, new: dict): for k, v in new.items(): self.__setitem__(key=k, value=v) def get(self, var, default=None): if var.startswith('var'): var = var[3:] while var.startswith('$'): var = var[1:] if var in self.vars: return self[var] else: return default def get_var(self, var): if var not in self.vars: self.vars[var] = Variable(name=var, value=None) return self.vars[var] def to_kwargs(self) -> dict: result = dict() for k, v in self.vars.items(): result[k] = v.value return resultPK!G  eeve/wrapper.pyfrom functools import wraps from copy import deepcopy from eeve.variable import VariableGroup from eeve.taskinfo import TaskInfo from typing import List import travel_backpack import sys global_variables = VariableGroup() global_variables['stdout'] = sys.stdout def action_wrapper(actions: 'List[Action]', debug=False): task_variables = VariableGroup() def start_task(**kwargs): print('task start') local_variables = VariableGroup() task_info = TaskInfo(actions=actions, global_variables=global_variables, task_variables=task_variables, local_variables=local_variables) local_variables.update(kwargs) #from pprint import pprint #pprint(task_info.__dict__) while task_info.current_action_index < len(task_info.actions): task_info.increment_action_index = True action = task_info.actions[task_info.current_action_index] print(f'executing action {task_info.current_action_index}: {action.name}') _run_args = deepcopy(action.run_args) _run_kwargs = deepcopy(action.run_kwargs) if debug: print('------------------------------------------------------------processing act', action) for i, arg in enumerate(_run_args): if type(arg) is str: if debug: print('------------------------------------------------------------processing arg', arg) if arg.startswith('var$') or arg.startswith('$'): _run_args[i] = task_info.get_var(arg) for k, v in _run_kwargs.items(): if type(v) is str: if debug: print('------------------------------------------------------------processing kwarg', v) if v.startswith('var$') or v.startswith('$'): _run_kwargs[k] = task_info.get_var(v) #print('updating task info') action.update_task_info(task_info) if debug: print('call:', action.name, _run_args, _run_kwargs) run_result = action.run(*_run_args, **_run_kwargs) if type(run_result) is dict: local_variables.update(run_result) if task_info.increment_action_index: task_info.current_action_index += 1 print('task end with index', task_info.current_action_index) print() return travel_backpack.except_and_print(travel_backpack.thread_encapsulation(start_task)) PK!:U&-&-eeve-1.0.0.dist-info/LICENSE Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. PK!HڽTUeeve-1.0.0.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!Heeve-1.0.0.dist-info/METADATASMo0 W`%X݂q0Q$IhkaXyI>>z:BdWxᛶT QKp_+uZІ>wk #[o\'L|u2+)J"+NfJgQ .I:J劲akʅ_L.dd5>Ru~l4ܪ02:F^2I=3~ҢSLkzWm->jd#nߚ >& QAd._eW|7\kQ=S,$g#W'-{z}M7Y We <)ɶRqdQsJVbEҡttIM+ӵ(&m2vwg|ݗ?VG?q4yF3D7/ЪQGcO3–$hN 6[N&HM0f2(~E}Yf{Һip`^p!Pr3j[40c>@0DY’0"hɾ.Hr\v'=.zoq~o|kz潎-UrL V 8Aںz} 1D+Ô;Fl;?d@Af@MH{(rh@o5Ĺ6vsc %=9^!huLOYD|R=jYy!KduY1F >p'}rYqk^}~>dѫr $#hLK˧Fo4_(إd舦ǽD5/d 2uA7cC2I ƥ9 zַrqH:-쫖&Q?ALYKF[r^u7e' ڄoyܻ;A1} OKd+Hv/HpNl6| < V<?5ɺGZӨo7o =ޜ军v F_YKG8Ky qkMЃ+s^*VvFee*ЮVX4[#*pf{ѫ6tx>OF$+@}$q<D!H(LsFu]o9=QthCWN/xyʇiL-Њ+ jI9`>qA6UL6$P`.CX2BVin1.<nͼttZ_`OTUq|\]+{J;t@ljIR򭎒4J~y_9=iӸܝN;9:a^?e[eoZ*?B< {,c@eeve/eeve actions/if_action.pyPK!yUJeeve/eeve actions/list_dir.pyPK!]gKeeve/eeve actions/play_audio.pyPK!qiِ#yLeeve/eeve actions/random_actions.pyPK!ׄYY!JMeeve/eeve actions/REST_request.pyPK!"Qeeve/eeve actions/start_process.pyPK!' v*OO 7Seeve/eeve actions/test_action.pyPK!@4>> Ueeve/eeve actions/wait_action.pyPK!;@Veeve/eeve events.txtPK!kjeeve/eeve eventsssss.txtPK!>T__!meeve/eeve plugins/tp-interface.pyPK!R00*eeve/eeve plugins/win_key_hook/__init__.pyPK!Y%%*eeve/eeve plugins/win_key_hook/__main__.pyPK!\ .eeve/eeve plugins/win_key_hook/input_parser.pyPK!)eEEKEK)eeve/eeve plugins/win_key_hook/key_map.pyPK!}Ũ..&x eeve/eeve triggers/broadcastCatcher.pyPK!~s9eeve/eeve triggers/rawinput.pyPK!gfs gUeeve/eeve triggers/structs.pyPK!0ugg#9aeeve/eeve triggers/timer_trigger.pyPK!>> eeeve/eeve.pyPK!EIfeeve/helpers.pyPK!nmeeve/importer.pyPK!$7Jreeve/mappings.pyPK!Pa txeeve/taskinfo.pyPK!:#eeve/variable.pyPK!G  eeve/wrapper.pyPK!:U&-&-Jeeve-1.0.0.dist-info/LICENSEPK!HڽTUeeve-1.0.0.dist-info/WHEELPK!H6eeve-1.0.0.dist-info/METADATAPK!H8^ aeeve-1.0.0.dist-info/RECORDPK""