Added message_bus module for module to module comms. Some changes to core_daemon and daemon_cli...

This commit is contained in:
kalzu rekku 2024-08-11 22:09:40 +03:00
parent a411cdee81
commit c5ded90841
7 changed files with 744 additions and 211 deletions

View File

@ -1,101 +1,35 @@
import os import os
import time import time
import sys import sys
import importlib
import socket import socket
import json import json
import threading import threading
import logging import logging
import traceback import traceback
import hashlib
from hook_manager import hook_manager
from module_manager import ModuleManager
# Configure logging # Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class ModuleManager:
def __init__(self, module_dirs):
self.module_dirs = module_dirs
self.loaded_modules = {}
self.extra_commands = {}
self._update_sys_path()
def _update_sys_path(self):
for dir in self.module_dirs:
full_path = os.path.abspath(dir)
if full_path not in sys.path:
sys.path.append(full_path)
logging.info(f"Added {full_path} to sys.path")
def add_module_dir(self, new_dir):
try:
full_path = os.path.abspath(new_dir)
if full_path not in self.module_dirs:
self.module_dirs.append(full_path)
self._update_sys_path()
return True, f"Added module directory: {full_path}"
return False, f"Module directory already exists: {full_path}"
except Exception as e:
logging.error(f"Error adding module directory: {str(e)}")
return False, f"Error adding module directory: {str(e)}"
def load_module(self, module_name):
for dir in self.module_dirs:
try:
module = importlib.import_module(f'{os.path.basename(dir)}.{module_name}')
self.loaded_modules[module_name] = module
if hasattr(module, 'initialize'):
module.initialize()
if hasattr(module, 'get_commands'):
new_commands = module.get_commands()
self.extra_commands.update(new_commands)
logging.info(f"Module '{module_name}' loaded successfully from {dir}.")
return True, f"Module '{module_name}' loaded and initialized successfully from {dir}."
except ImportError:
continue
except Exception as e:
logging.error(f"Error loading module '{module_name}' from {dir}: {str(e)}")
return False, f"Error loading module '{module_name}' from {dir}: {str(e)}"
return False, f"Error: Unable to load module '{module_name}' from any of the module directories."
def unload_module(self, module_name):
if module_name in self.loaded_modules:
try:
module = self.loaded_modules[module_name]
if hasattr(module, 'shutdown'):
module.shutdown()
if hasattr(module, 'get_commands'):
commands_to_remove = module.get_commands().keys()
for cmd in commands_to_remove:
self.extra_commands.pop(cmd, None)
del self.loaded_modules[module_name]
logging.info(f"Module '{module_name}' unloaded successfully.")
return True, f"Module '{module_name}' unloaded and shut down."
except Exception as e:
logging.error(f"Error unloading module '{module_name}': {str(e)}")
return False, f"Error unloading module '{module_name}': {str(e)}"
return False, f"Module '{module_name}' is not loaded."
def list_modules(self):
return list(self.loaded_modules.keys())
def list_commands(self):
return list(self.extra_commands.keys())
def execute_command(self, command, args):
if command in self.extra_commands:
try:
result = self.extra_commands[command](args)
return True, result
except Exception as e:
logging.error(f"Error executing command '{command}': {str(e)}")
return False, f"Error executing command '{command}': {str(e)}"
return False, "Command not found"
class CoreDaemon: class CoreDaemon:
def __init__(self, host='localhost', port=9999, module_dirs=None): def __init__(self, host='localhost', port=9999, module_dirs=None):
self.host = host self.host = host
self.port = port self.port = port
self.module_dirs = module_dirs or ['modules'] self.module_dirs = module_dirs or ['modules']
self.module_manager = ModuleManager(self.module_dirs) self.module_manager = ModuleManager(self.module_dirs)
self.script_path = sys.argv[0]
self.initial_hash = self.calculate_file_hash(self.script_path)
self.hook_manager = hook_manager
def calculate_file_hash(self, file_path):
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def start(self): def start(self):
try: try:
@ -136,45 +70,80 @@ class CoreDaemon:
logging.error(f"Error handling client request: {str(e)}") logging.error(f"Error handling client request: {str(e)}")
conn.sendall(json.dumps({'success': False, 'message': 'Internal server error'}).encode()) conn.sendall(json.dumps({'success': False, 'message': 'Internal server error'}).encode())
def check_for_updates(self):
logging.info("Checking for updates...")
script_modified = False
current_script_hash = self.calculate_file_hash(self.script_path)
if current_script_hash != self.initial_hash:
script_modified = True
modified_modules = self.module_manager.check_modules_modified()
if script_modified or modified_modules:
message = "Updates detected:\n"
if script_modified:
message += "- Core script has been modified\n"
if modified_modules:
message += f"- Modified modules: {', '.join(modified_modules)}\n"
return True, message
else:
return False, "No updates detected."
def upgrade(self): def upgrade(self):
logging.info("Upgrading core daemon...") update_available, message = self.check_for_updates()
try: if update_available:
# Here you would typically download or copy the new version logging.info(message)
# Here you would typically perform any necessary upgrade steps
# For this example, we'll just restart the script # For this example, we'll just restart the script
os.execv(sys.executable, ['python'] + sys.argv) os.execv(sys.executable, ['python'] + sys.argv)
except Exception as e: return True, "Upgrade initiated. Restarting with new version."
logging.error(f"Error during upgrade: {str(e)}") else:
return False, f"Upgrade failed: {str(e)}" logging.info("No updates detected. Skipping upgrade.")
return False, "No upgrade needed. Current version is up to date."
def process_command(self, command): def process_command(self, command):
action = command.get('action') action = command.get('action')
# Execute pre-command hooks
pre_command_results = self.hook_manager.execute_hook('pre_command', command)
for result in pre_command_results:
if result is False:
return {'success': False, 'message': "Command rejected by pre-command hook"}
try: try:
if action == 'load': if action == 'load':
return self.module_manager.load_module(command.get('module')) success, message = self.module_manager.load_module(command.get('module'))
elif action == 'unload': elif action == 'unload':
return self.module_manager.unload_module(command.get('module')) success, message = self.module_manager.unload_module(command.get('module'))
elif action == 'list': elif action == 'list':
modules = self.module_manager.list_modules() success, modules = True, self.module_manager.list_modules()
return True, modules message = modules
elif action == 'execute': elif action == 'execute':
return self.module_manager.execute_command(command.get('command'), command.get('args')) success, message = self.module_manager.execute_command(command.get('command'), command.get('args'))
elif action == 'upgrade': elif action == 'upgrade':
return self.upgrade() success, message = self.upgrade()
elif action == 'list_commands': elif action == 'list_commands':
commands = self.module_manager.list_commands() success, commands = True, self.module_manager.list_commands()
return True, commands message = commands
elif action == 'add_module_dir': elif action == 'add_module_dir':
return self.module_manager.add_module_dir(command.get('dir')) success, message = self.module_manager.add_module_dir(command.get('dir'))
elif action == 'shutdown': elif action == 'shutdown':
response = self.shutdown() response = self.shutdown()
# After sending the response, exit the program # After sending the response, exit the program
threading.Thread(target=self._delayed_shutdown).start() threading.Thread(target=self._delayed_shutdown).start()
return response return response
else: else:
return False, "Unknown command" success, message = False, "Unknown command"
except Exception as e: except Exception as e:
logging.error(f"Error processing command {action}: {str(e)}") logging.error(f"Error processing command {action}: {str(e)}")
return False, f"Error processing command {action}: {str(e)}" success, message = False, f"Error processing command: {str(e)}"
response = {'success': success, 'message': message}
# Execute post-command hooks
self.hook_manager.execute_hook('post_command', command, response)
return response
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse

View File

@ -1,7 +1,6 @@
import cmd import cmd
import socket import socket
import json import json
import time
import os import os
class ModuleShell(cmd.Cmd): class ModuleShell(cmd.Cmd):
@ -12,134 +11,66 @@ class ModuleShell(cmd.Cmd):
super().__init__() super().__init__()
self.host = host self.host = host
self.port = port self.port = port
self.update_commands() self.session_id = None
def send_command(self, command, retries=3, delay=2): def send_command(self, action, **kwargs):
for attempt in range(retries): command = {'action': action, **kwargs}
try: if self.session_id:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: command['session_id'] = self.session_id
s.settimeout(5) # Set a 5-second timeout
s.connect((self.host, self.port))
s.sendall(json.dumps(command).encode())
data = s.recv(1024).decode()
if data:
return json.loads(data)
else:
print("Received empty response. The server might be restarting.")
if command['action'] == 'upgrade':
return {'success': True, 'message': 'Upgrade initiated. The server is restarting.'}
except (socket.timeout, ConnectionRefusedError, json.JSONDecodeError) as e:
if attempt < retries - 1:
print(f"Connection failed. Retrying in {delay} seconds...")
time.sleep(delay)
else:
print(f"Failed to connect after {retries} attempts: {str(e)}")
return {'success': False, 'message': f"Connection error: {str(e)}"}
return {'success': False, 'message': "Failed to connect to the server."}
def update_commands(self):
try: try:
response = self.send_command({'action': 'list_commands'}) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
if isinstance(response, dict) and response.get('success'): s.connect((self.host, self.port))
self.dynamic_commands = response['message'] s.sendall(json.dumps(command).encode())
elif isinstance(response, list): data = s.recv(1024).decode()
self.dynamic_commands = response return json.loads(data) if data else None
else:
print("Unexpected response format when updating commands.")
self.dynamic_commands = []
except Exception as e: except Exception as e:
print(f"Error updating commands: {str(e)}") print(f"Error: {str(e)}")
self.dynamic_commands = [] return None
def default(self, line):
parts = line.split()
command = parts[0]
args = ' '.join(parts[1:])
response = self.send_command({'action': 'execute', 'command': command, 'args': args})
if isinstance(response, dict):
if response.get('success'):
print(response['message'])
else:
print(f"Error: {response.get('message', 'Unknown error')}")
else:
print(f"Unexpected response: {response}")
def completenames(self, text, *ignored):
dotext = 'do_'+text
return [a[3:] for a in self.get_names() if a.startswith(dotext)] + \
[cmd for cmd in self.dynamic_commands if cmd.startswith(text)]
def do_load(self, arg): def do_load(self, arg):
"""Load a module: load <module_name>""" """Load a module: load <module_name>"""
response = self.send_command({'action': 'load', 'module': arg}) response = self.send_command('load', module=arg)
if isinstance(response, dict): if response:
print(response.get('message', 'Unknown response')) print(response.get('message', 'Unknown response'))
else:
print(f"Unexpected response: {response}")
self.update_commands() # Update commands after loading a new module
def do_unload(self, arg): def do_unload(self, arg):
"""Unload a module: unload <module_name>""" """Unload a module: unload <module_name>"""
response = self.send_command({'action': 'unload', 'module': arg}) response = self.send_command('unload', module=arg)
if isinstance(response, dict): if response:
print(response.get('message', 'Unknown response')) print(response.get('message', 'Unknown response'))
else:
print(f"Unexpected response: {response}")
self.update_commands() # Update commands after unloading a module
def do_list(self, arg): def do_list(self, arg):
"""List all loaded modules""" """List all loaded modules"""
response = self.send_command({'action': 'list'}) response = self.send_command('list')
if isinstance(response, dict) and response.get('success'): if response and response.get('success'):
modules = response['message'] modules = response.get('message', [])
elif isinstance(response, list): if modules:
modules = response print("Loaded modules:")
for module in modules:
print(f"- {module}")
else:
print("No modules are currently loaded.")
else: else:
print("Unexpected response format when listing modules.") print("Failed to retrieve module list.")
def do_execute(self, arg):
"""Execute a command: execute <command> [args]"""
parts = arg.split()
if not parts:
print("Error: No command specified.")
return return
command = parts[0]
if modules: args = ' '.join(parts[1:])
print("Loaded modules:") response = self.send_command('execute', command=command, args=args)
for module in modules: if response:
print(f"- {module}")
else:
print("No modules are currently loaded.")
def do_list_available(self, arg):
"""List available modules in the modules folder"""
modules_path = os.path.join(os.path.dirname(__file__), 'modules')
available_modules = [f[:-3] for f in os.listdir(modules_path) if f.endswith('.py') and not f.startswith('__')]
print("Available modules:")
for module in available_modules:
print(f"- {module}")
def complete_load(self, text, line, begidx, endidx):
modules_path = os.path.join(os.path.dirname(__file__), 'modules')
available_modules = [f[:-3] for f in os.listdir(modules_path) if f.endswith('.py') and not f.startswith('__')]
return [m for m in available_modules if m.startswith(text)]
def do_add_module_dir(self, arg):
"""Add a new module directory: add_module_dir <directory_path>"""
if not arg:
print("Error: Please provide a directory path.")
return
response = self.send_command({'action': 'add_module_dir', 'dir': arg})
if isinstance(response, dict):
print(response.get('message', 'Unknown response')) print(response.get('message', 'Unknown response'))
else:
print(f"Unexpected response: {response}")
def do_upgrade(self, arg): def do_upgrade(self, arg):
"""Upgrade the core daemon""" """Upgrade the core daemon and modules"""
response = self.send_command({'action': 'upgrade'}) response = self.send_command('upgrade')
if isinstance(response, list): if response:
try: print(response.get('message', 'Unknown response'))
print(response[0]['message'])
except Exception:
print('Core daemon response did not make sense.')
print("Please reconnect in a few seconds.")
return True # This will exit the command loop
def do_exit(self, arg): def do_exit(self, arg):
"""Exit the shell""" """Exit the shell"""
@ -148,13 +79,15 @@ class ModuleShell(cmd.Cmd):
def do_shutdown(self, arg): def do_shutdown(self, arg):
"""Shutdown the core daemon""" """Shutdown the core daemon"""
response = self.send_command({'action': 'shutdown'}) response = self.send_command('shutdown')
if isinstance(response, dict): if response:
print(response.get('message', 'Unknown response')) print(response.get('message', 'Unknown response'))
else:
print(f"Unexpected response: {response}")
print("Core daemon is shutting down. Exiting CLI.") print("Core daemon is shutting down. Exiting CLI.")
return True # This will exit the command loop return True
def default(self, line):
"""Handle unknown commands by trying to execute them"""
self.do_execute(line)
def main(): def main():
ModuleShell().cmdloop() ModuleShell().cmdloop()

110
hook_manager.py Normal file
View File

@ -0,0 +1,110 @@
"""
hook_manager.py
This module provides the HookManager class which allows for the registration,
unregistration, and execution of hooks. Hooks are functions that can be executed
at specific points in the program flow, such as before or after commands or module
loading/unloading.
Classes:
HookManager: Manages the registration, unregistration, and execution of hooks.
Variables:
hook_manager (HookManager): An instance of HookManager for global usage.
"""
import logging
from typing import Callable, List, Dict
class HookManager:
"""
A manager for handling hooks that can be registered, unregistered, and executed
at various points in a program's flow.
Attributes:
hooks (dict): A dictionary containing lists of functions for each hook point.
"""
def __init__(self):
"""
Initializes a new instance of the HookManager class with predefined hook points.
"""
self.hooks: Dict[str, List[Callable]] = {
'pre_command': [],
'post_command': [],
'pre_module_load': [],
'post_module_load': [],
'pre_module_unload': [],
'post_module_unload': [],
}
logging.info("HookManager initialized with hooks: %s", list(self.hooks.keys()))
def register_hook(self, hook_name: str, func: Callable):
"""
Registers a function to a specified hook.
Args:
hook_name (str): The name of the hook to register the function to.
func (function): The function to register.
Raises:
ValueError: If the specified hook name is not recognized.
"""
if hook_name in self.hooks:
self.hooks[hook_name].append(func)
logging.info("Registered function %s to hook %s", func.__name__, hook_name)
else:
logging.error("Attempted to register to unknown hook: %s", hook_name)
raise ValueError(f"Unknown hook: {hook_name}")
def unregister_hook(self, hook_name: str, func: Callable):
"""
Unregisters a function from a specified hook.
Args:
hook_name (str): The name of the hook to unregister the function from.
func (function): The function to unregister.
Raises:
ValueError: If the specified hook name is not recognized.
"""
if hook_name in self.hooks:
if func in self.hooks[hook_name]:
self.hooks[hook_name].remove(func)
logging.info("Unregistered function %s from hook %s", func.__name__, hook_name)
else:
logging.warning("Function %s not found in hook %s", func.__name__, hook_name)
else:
logging.error("Attempted to unregister from unknown hook: %s", hook_name)
raise ValueError(f"Unknown hook: {hook_name}")
def execute_hook(self, hook_name: str, *args, **kwargs) -> List:
"""
Executes all functions registered to a specified hook with the provided arguments.
Args:
hook_name (str): The name of the hook to execute the functions for.
*args: Variable length argument list to pass to the hook functions.
**kwargs: Arbitrary keyword arguments to pass to the hook functions.
Returns:
list: A list of results from each hook function executed.
Raises:
ValueError: If the specified hook name is not recognized.
"""
if hook_name not in self.hooks:
logging.error("Attempted to execute unknown hook: %s", hook_name)
raise ValueError(f"Unknown hook: {hook_name}")
results = []
for func in self.hooks[hook_name]:
try:
result = func(*args, **kwargs)
results.append(result)
logging.info("Executed function %s in hook %s", func.__name__, hook_name)
except Exception as e:
logging.error("Error executing function %s in hook %s: %s", func.__name__, hook_name, str(e))
return results
hook_manager = HookManager()

252
module_manager.py Normal file
View File

@ -0,0 +1,252 @@
"""
module_manager.py
This module provides the ModuleManager class which allows for the management
of dynamically loadable modules. The ModuleManager supports loading, unloading,
and execution of modules, as well as handling module-specific commands and hooks.
Classes:
ModuleManager: Manages loading, unloading, and executing modules and their commands.
Variables:
hook_manager (HookManager): An instance of HookManager for managing hooks.
"""
import os
import sys
import importlib
import logging
import hashlib
from hook_manager import HookManager
hook_manager = HookManager()
class ModuleManager:
"""
A manager for handling dynamically loadable modules, including loading, unloading,
and executing module-specific commands and hooks.
Attributes:
module_dirs (list): A list of directories to search for modules.
loaded_modules (dict): A dictionary of loaded modules.
extra_commands (dict): A dictionary of additional commands provided by modules.
module_hashes (dict): A dictionary storing file paths and their hashes.
hook_manager (HookManager): An instance of HookManager for managing hooks.
"""
def __init__(self, module_dirs):
"""
Initializes a new instance of the ModuleManager class.
Args:
module_dirs (list): A list of directories to search for modules.
"""
self.module_dirs = module_dirs
self.loaded_modules = {}
self.extra_commands = {}
self.module_hashes = {} # store file paths and hashes
self._update_sys_path()
self.hook_manager = hook_manager # Use the global hook_manager!
def _update_sys_path(self):
"""
Updates the system path to include the directories in module_dirs.
"""
for dir in self.module_dirs:
full_path = os.path.abspath(dir)
if full_path not in sys.path:
sys.path.append(full_path)
logging.info(f"Added {full_path} to sys.path")
def add_module_dir(self, new_dir):
"""
Adds a new directory to the list of module directories and updates the system path.
Args:
new_dir (str): The new directory to add.
Returns:
tuple: A tuple containing a boolean indicating success and a message.
"""
try:
full_path = os.path.abspath(new_dir)
if full_path not in self.module_dirs:
self.module_dirs.append(full_path)
self._update_sys_path()
return True, f"Added module directory: {full_path}"
return False, f"Module directory already exists: {full_path}"
except Exception as e:
logging.error(f"Error adding module directory: {str(e)}")
return False, f"Error adding module directory: {str(e)}"
def calculate_file_hash(self, file_path):
"""
Calculates the SHA-256 hash of a file.
Args:
file_path (str): The path to the file.
Returns:
str: The SHA-256 hash of the file.
"""
sha256_hash = hashlib.sha256()
try:
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
except FileNotFoundError:
logging.error(f"File not found: {file_path}")
return None
except Exception as e:
logging.error(f"Error calculating hash for file '{file_path}': {str(e)}")
return None
def load_module(self, module_name):
"""
Loads a module by name, initializing it and registering its commands and hooks.
Args:
module_name (str): The name of the module to load.
Returns:
tuple: A tuple containing a boolean indicating success and a message.
"""
self.hook_manager.execute_hook('pre_module_load', module_name)
for dir in self.module_dirs:
try:
module_path = os.path.join(dir, f"{module_name}.py")
if not os.path.exists(module_path):
continue
module = importlib.import_module(f'{os.path.basename(dir)}.{module_name}')
self.loaded_modules[module_name] = module
# Calculate and store the hash of the module file
module_hash = self.calculate_file_hash(module_path)
if module_hash:
self.module_hashes[module_path] = module_hash
self._initialize_module(module)
self.hook_manager.execute_hook('post_module_load', module_name)
return True, f"Module '{module_name}' loaded and initialized successfully from {dir}."
except ImportError as e:
logging.error(f"ImportError for module '{module_name}' from {dir}: {str(e)}")
continue
except Exception as e:
logging.error(f"Error loading module '{module_name}' from {dir}: {str(e)}")
return False, f"Error loading module '{module_name}' from {dir}: {str(e)}"
return False, f"Error: Unable to load module '{module_name}' from any of the module directories."
def _initialize_module(self, module):
"""
Initializes a loaded module by calling its initialize method, registering commands, and registering hooks.
Args:
module: The loaded module.
"""
if hasattr(module, 'initialize'):
module.initialize()
if hasattr(module, 'get_commands'):
new_commands = module.get_commands()
self.extra_commands.update(new_commands)
if hasattr(module, 'register_hooks'):
module.register_hooks(self.hook_manager)
def unload_module(self, module_name):
"""
Unloads a module by name, shutting it down and removing its commands and hooks.
Args:
module_name (str): The name of the module to unload.
Returns:
tuple: A tuple containing a boolean indicating success and a message.
"""
self.hook_manager.execute_hook('pre_module_unload', module_name)
if module_name in self.loaded_modules:
try:
module = self.loaded_modules[module_name]
self._shutdown_module(module)
self.hook_manager.execute_hook('post_module_unload', module_name)
# Remove the module's hash from our tracking
module_path = module.__file__
self.module_hashes.pop(module_path, None)
del self.loaded_modules[module_name]
logging.info(f"Module '{module_name}' unloaded successfully.")
return True, f"Module '{module_name}' unloaded and shut down."
except Exception as e:
logging.error(f"Error unloading module '{module_name}': {str(e)}")
return False, f"Error unloading module '{module_name}': {str(e)}"
return False, f"Module '{module_name}' is not loaded."
def _shutdown_module(self, module):
"""
Shuts down a loaded module by calling its shutdown method and removing its commands and hooks.
Args:
module: The loaded module.
"""
if hasattr(module, 'shutdown'):
module.shutdown()
if hasattr(module, 'get_commands'):
commands_to_remove = module.get_commands().keys()
for cmd in commands_to_remove:
self.extra_commands.pop(cmd, None)
if hasattr(module, 'unregister_hooks'):
module.unregister_hooks(self.hook_manager)
def list_modules(self):
"""
Lists all currently loaded modules.
Returns:
list: A list of names of loaded modules.
"""
return list(self.loaded_modules.keys())
def list_commands(self):
"""
Lists all commands provided by the loaded modules.
Returns:
list: A list of command names.
"""
return list(self.extra_commands.keys())
def execute_command(self, command, args):
"""
Executes a command provided by a loaded module.
Args:
command (str): The name of the command to execute.
args (list): The arguments to pass to the command.
Returns:
tuple: A tuple containing a boolean indicating success and the command result or an error message.
"""
if command in self.extra_commands:
try:
result = self.extra_commands[command](args)
return True, result
except Exception as e:
logging.error(f"Error executing command '{command}': {str(e)}")
return False, f"Error executing command '{command}': {str(e)}"
return False, f"Command '{command}' not found"
def check_modules_modified(self):
"""
Checks if any loaded modules have been modified on disk by comparing file hashes.
Returns:
list: A list of names of modified modules.
"""
modified_modules = []
for module_path, stored_hash in self.module_hashes.items():
current_hash = self.calculate_file_hash(module_path)
if current_hash and current_hash != stored_hash:
module_name = os.path.splitext(os.path.basename(module_path))[0]
modified_modules.append(module_name)
return modified_modules

95
modules/message_bus.py Normal file
View File

@ -0,0 +1,95 @@
import queue
import threading
class MessageBus:
def __init__(self):
self.queues = {}
self.lock = threading.Lock()
def create_queue(self, queue_name):
with self.lock:
if queue_name not in self.queues:
self.queues[queue_name] = queue.Queue()
return True
return False
def delete_queue(self, queue_name):
with self.lock:
if queue_name in self.queues:
del self.queues[queue_name]
return True
return False
def send_message(self, queue_name, message):
with self.lock:
if queue_name in self.queues:
self.queues[queue_name].put(message)
return True
return False
def receive_message(self, queue_name, timeout=None):
with self.lock:
if queue_name in self.queues:
try:
return self.queues[queue_name].get(timeout=timeout)
except queue.Empty:
return None
return None
message_bus = MessageBus()
def do_create_queue(args):
"""Create a new message queue: create_queue <queue_name>"""
queue_name = args.strip()
if message_bus.create_queue(queue_name):
return f"Queue '{queue_name}' created successfully"
else:
return f"Queue '{queue_name}' already exists"
def do_delete_queue(args):
"""Delete a message queue: delete_queue <queue_name>"""
queue_name = args.strip()
if message_bus.delete_queue(queue_name):
return f"Queue '{queue_name}' deleted successfully"
else:
return f"Queue '{queue_name}' does not exist"
def do_send_message(args):
"""Send a message to a queue: send_message <queue_name> <message>"""
parts = args.split(maxsplit=1)
if len(parts) != 2:
return "Invalid arguments. Usage: send_message <queue_name> <message>"
queue_name, message = parts
if message_bus.send_message(queue_name, message):
return f"Message sent to queue '{queue_name}'"
else:
return f"Queue '{queue_name}' does not exist"
def do_receive_message(args):
"""Receive a message from a queue: receive_message <queue_name> [timeout]"""
parts = args.split()
if len(parts) not in (1, 2):
return "Invalid arguments. Usage: receive_message <queue_name> [timeout]"
queue_name = parts[0]
timeout = float(parts[1]) if len(parts) == 2 else None
message = message_bus.receive_message(queue_name, timeout)
if message is not None:
return f"Received message from queue '{queue_name}': {message}"
else:
return f"No message available in queue '{queue_name}'"
commands = {
'create_queue': do_create_queue,
'delete_queue': do_delete_queue,
'send_message': do_send_message,
'receive_message': do_receive_message,
}
def get_commands():
return commands
def initialize():
print("Message bus module initialized")
def shutdown():
print("Message bus module shut down")

View File

@ -0,0 +1,49 @@
message_bus = None
def set_message_bus(bus):
global message_bus
message_bus = bus
def do_something_and_notify(args):
# Do something...
result = "Operation completed"
# Notify another module if message bus is available
if message_bus:
message_bus.send_message('notifications', f"Operation result: {result}")
return "Operation completed and notification sent"
else:
return "Operation completed (notification not sent, message bus not available)"
def do_check_notifications(args):
if not message_bus:
return "Message bus not available"
message = message_bus.receive_message('notifications', timeout=1)
if message:
return f"Received notification: {message}"
else:
return "No new notifications"
commands = {
'do_something': do_something_and_notify,
'check_notifications': do_check_notifications,
}
def get_commands():
return commands
def initialize():
global message_bus
try:
from message_bus import message_bus as mb
message_bus = mb
message_bus.create_queue('notifications')
print("Example module initialized with message bus")
except ImportError:
print("Example module initialized without message bus")
def shutdown():
if message_bus:
message_bus.delete_queue('notifications')
print("Example module shut down")

125
modules/user_auth.py Normal file
View File

@ -0,0 +1,125 @@
import hashlib
import uuid
import time
class UserAuth:
def __init__(self):
self.users = {} # Store users as {username: {password_hash, salt}}
self.sessions = {} # Store sessions as {session_id: {username, expiry}}
self.session_duration = 3600 # 1 hour
def hash_password(self, password, salt=None):
if salt is None:
salt = uuid.uuid4().hex
return hashlib.sha256((password + salt).encode()).hexdigest(), salt
def register_user(self, username, password):
if username in self.users:
return False, "User already exists"
password_hash, salt = self.hash_password(password)
self.users[username] = {"password_hash": password_hash, "salt": salt}
return True, "User registered successfully"
def authenticate(self, username, password):
if username not in self.users:
return False, "User not found"
user = self.users[username]
password_hash, _ = self.hash_password(password, user["salt"])
if password_hash == user["password_hash"]:
session_id = uuid.uuid4().hex
expiry = time.time() + self.session_duration
self.sessions[session_id] = {"username": username, "expiry": expiry}
return True, session_id
return False, "Invalid password"
def authenticate_request(self, session_id, action):
if action in ['register', 'login']: # These actions don't require authentication
return True, None
success, result = self.validate_session(session_id)
if not success:
return False, "Authentication required"
return True, result # result here is the username
def validate_session(self, session_id):
if session_id not in self.sessions:
return False, "Invalid session"
session = self.sessions[session_id]
if time.time() > session["expiry"]:
del self.sessions[session_id]
return False, "Session expired"
return True, session["username"]
def logout(self, session_id):
if session_id in self.sessions:
del self.sessions[session_id]
return True, "Logged out successfully"
return False, "Invalid session"
user_auth = UserAuth()
def do_register(args):
"""Register a new user: register <username> <password>"""
try:
username, password = args.split()
success, message = user_auth.register_user(username, password)
return message
except ValueError:
return "Invalid arguments. Usage: register <username> <password>"
def do_login(args):
"""Login a user: login <username> <password>"""
try:
username, password = args.split()
success, result = user_auth.authenticate(username, password)
if success:
return f"Login successful. Session ID: {result}"
return result
except ValueError:
return "Invalid arguments. Usage: login <username> <password>"
def do_validate(args):
"""Validate a session: validate <session_id>"""
success, result = user_auth.validate_session(args)
if success:
return f"Valid session for user: {result}"
return result
def do_logout(args):
"""Logout a user: logout <session_id>"""
success, message = user_auth.logout(args)
return message
def auth_pre_command_hook(command):
action = command.get('action')
session_id = command.get('session_id')
if action in ['register', 'login']:
return True
success, result = user_auth.validate_session(session_id)
if not success:
return False
return True
commands = {
'register': do_register,
'login': do_login,
'validate': do_validate,
'logout': do_logout,
}
def get_commands():
return commands
def initialize():
print("User authentication module initialized")
def shutdown():
print("User authentication module shut down")
def register_hooks(hook_manager):
hook_manager.register_hook('pre_command', auth_pre_command_hook)
def unregister_hooks(hook_manager):
hook_manager.unregister_hook('pre_command', auth_pre_command_hook)