Added Additional Macro Support
This commit is contained in:
@ -11,6 +11,7 @@ from io import BytesIO
|
|||||||
import base64
|
import base64
|
||||||
import traceback
|
import traceback
|
||||||
import platform # OS Detection
|
import platform # OS Detection
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
import socketio
|
import socketio
|
||||||
from qasync import QEventLoop
|
from qasync import QEventLoop
|
||||||
@ -78,6 +79,21 @@ class ConfigManager:
|
|||||||
CONFIG = ConfigManager(CONFIG_PATH)
|
CONFIG = ConfigManager(CONFIG_PATH)
|
||||||
CONFIG.load()
|
CONFIG.load()
|
||||||
|
|
||||||
|
def init_agent_id():
|
||||||
|
if not CONFIG.data.get('agent_id'):
|
||||||
|
CONFIG.data['agent_id'] = f"{socket.gethostname().lower()}-agent-{uuid.uuid4().hex[:8]}"
|
||||||
|
CONFIG._write()
|
||||||
|
return CONFIG.data['agent_id']
|
||||||
|
|
||||||
|
AGENT_ID = init_agent_id()
|
||||||
|
print(f"[DEBUG] Using AGENT_ID: {AGENT_ID}")
|
||||||
|
|
||||||
|
def clear_regions_only():
|
||||||
|
CONFIG.data['regions'] = CONFIG.data.get('regions', {})
|
||||||
|
CONFIG._write()
|
||||||
|
|
||||||
|
clear_regions_only()
|
||||||
|
|
||||||
# //////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
# CORE SECTION: OPERATING SYSTEM DETECTION
|
# CORE SECTION: OPERATING SYSTEM DETECTION
|
||||||
# //////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
@ -96,22 +112,15 @@ CONFIG.data['agent_operating_system'] = detect_agent_os()
|
|||||||
CONFIG._write()
|
CONFIG._write()
|
||||||
|
|
||||||
# //////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
|
# CORE SECTION: MACRO AUTOMATION
|
||||||
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
|
MACRO_ENGINE_PATH = os.path.join(os.path.dirname(__file__), "Python_API_Endpoints", "macro_engines.py")
|
||||||
|
spec = importlib.util.spec_from_file_location("macro_engines", MACRO_ENGINE_PATH)
|
||||||
|
macro_engines = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(macro_engines)
|
||||||
|
|
||||||
def init_agent_id():
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
if not CONFIG.data.get('agent_id'):
|
# CORE SECTION: ASYNC TASK / WEBSOCKET
|
||||||
CONFIG.data['agent_id'] = f"{socket.gethostname().lower()}-agent-{uuid.uuid4().hex[:8]}"
|
|
||||||
CONFIG._write()
|
|
||||||
return CONFIG.data['agent_id']
|
|
||||||
|
|
||||||
AGENT_ID = init_agent_id()
|
|
||||||
print(f"[DEBUG] Using AGENT_ID: {AGENT_ID}")
|
|
||||||
|
|
||||||
def clear_regions_only():
|
|
||||||
CONFIG.data['regions'] = CONFIG.data.get('regions', {})
|
|
||||||
CONFIG._write()
|
|
||||||
|
|
||||||
clear_regions_only()
|
|
||||||
|
|
||||||
# //////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
sio = socketio.AsyncClient(reconnection=True, reconnection_attempts=0, reconnection_delay=5)
|
sio = socketio.AsyncClient(reconnection=True, reconnection_attempts=0, reconnection_delay=5)
|
||||||
@ -146,6 +155,10 @@ async def disconnect():
|
|||||||
CONFIG.data['regions'].clear()
|
CONFIG.data['regions'].clear()
|
||||||
CONFIG._write()
|
CONFIG._write()
|
||||||
|
|
||||||
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
|
# CORE SECTION: AGENT CONFIG MANAGEMENT / WINDOW MANAGEMENT
|
||||||
|
# //////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@sio.on('agent_config')
|
@sio.on('agent_config')
|
||||||
async def on_agent_config(cfg):
|
async def on_agent_config(cfg):
|
||||||
print("[DEBUG] agent_config event received.")
|
print("[DEBUG] agent_config event received.")
|
||||||
@ -155,6 +168,15 @@ async def on_agent_config(cfg):
|
|||||||
await stop_all_roles()
|
await stop_all_roles()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@sio.on('list_agent_windows')
|
||||||
|
async def handle_list_agent_windows(data):
|
||||||
|
# Called from the server/webui to fetch current open windows for dropdown
|
||||||
|
windows = macro_engines.list_windows()
|
||||||
|
await sio.emit('agent_window_list', {
|
||||||
|
'agent_id': AGENT_ID,
|
||||||
|
'windows': windows
|
||||||
|
})
|
||||||
|
|
||||||
print(f"[CONFIG] Received New Agent Config with {len(roles)} Role(s).")
|
print(f"[CONFIG] Received New Agent Config with {len(roles)} Role(s).")
|
||||||
|
|
||||||
new_ids = {r.get('node_id') for r in roles if r.get('node_id')}
|
new_ids = {r.get('node_id') for r in roles if r.get('node_id')}
|
||||||
@ -179,10 +201,15 @@ async def on_agent_config(cfg):
|
|||||||
|
|
||||||
for role_cfg in roles:
|
for role_cfg in roles:
|
||||||
nid = role_cfg.get('node_id')
|
nid = role_cfg.get('node_id')
|
||||||
if role_cfg.get('role') == 'screenshot':
|
role = role_cfg.get('role')
|
||||||
|
if role == 'screenshot':
|
||||||
print(f"[DEBUG] Starting screenshot task for {nid}")
|
print(f"[DEBUG] Starting screenshot task for {nid}")
|
||||||
task = asyncio.create_task(screenshot_task(role_cfg))
|
task = asyncio.create_task(screenshot_task(role_cfg))
|
||||||
role_tasks[nid] = task
|
role_tasks[nid] = task
|
||||||
|
elif role == 'macro':
|
||||||
|
print(f"[DEBUG] Starting macro task for {nid}")
|
||||||
|
task = asyncio.create_task(macro_task(role_cfg))
|
||||||
|
role_tasks[nid] = task
|
||||||
|
|
||||||
# ---------------- Overlay Widget ----------------
|
# ---------------- Overlay Widget ----------------
|
||||||
overlay_green_thickness = 4
|
overlay_green_thickness = 4
|
||||||
@ -342,6 +369,43 @@ async def screenshot_task(cfg):
|
|||||||
print(f"[ERROR] Screenshot task {nid} failed: {e}")
|
print(f"[ERROR] Screenshot task {nid} failed: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# ---------------- Macro Task ----------------
|
||||||
|
async def macro_task(cfg):
|
||||||
|
nid = cfg.get('node_id')
|
||||||
|
window_handle = cfg.get('window_handle')
|
||||||
|
mode = cfg.get('operation_mode', 'keypress') # 'keypress' or 'typed_text'
|
||||||
|
key = cfg.get('key')
|
||||||
|
text = cfg.get('text')
|
||||||
|
interval_ms = int(cfg.get('interval_ms', 1000))
|
||||||
|
randomize = cfg.get('randomize_interval', False)
|
||||||
|
random_min = int(cfg.get('random_min', 750))
|
||||||
|
random_max = int(cfg.get('random_max', 950))
|
||||||
|
active = cfg.get('active', True) # Whether macro is "started" or "paused"
|
||||||
|
print(f"[DEBUG] Macro task for node {nid} started on window {window_handle}")
|
||||||
|
|
||||||
|
import random
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
if not active:
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
continue
|
||||||
|
if mode == 'keypress' and key:
|
||||||
|
macro_engines.send_keypress_to_window(window_handle, key)
|
||||||
|
elif mode == 'typed_text' and text:
|
||||||
|
macro_engines.type_text_to_window(window_handle, text)
|
||||||
|
# Interval logic
|
||||||
|
if randomize:
|
||||||
|
ms = random.randint(random_min, random_max)
|
||||||
|
else:
|
||||||
|
ms = interval_ms
|
||||||
|
await asyncio.sleep(ms / 1000.0)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
print(f"[TASK] Macro role {nid} cancelled.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Macro task {nid} failed: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
# ---------------- Config Watcher ----------------
|
# ---------------- Config Watcher ----------------
|
||||||
async def config_watcher():
|
async def config_watcher():
|
||||||
print("[DEBUG] Starting config watcher")
|
print("[DEBUG] Starting config watcher")
|
||||||
|
Reference in New Issue
Block a user