import json from dataclasses import dataclass, field from astrbot.api import FunctionTool from astrbot.core.agent.run_context import ContextWrapper from astrbot.core.agent.tool import ToolExecResult from astrbot.core.astr_agent_context import AstrAgentContext from ..computer_client import get_booter, get_local_booter def _check_admin_permission(context: ContextWrapper[AstrAgentContext]) -> str | None: cfg = context.context.context.get_config( umo=context.context.event.unified_msg_origin ) provider_settings = cfg.get("provider_settings", {}) require_admin = provider_settings.get("computer_use_require_admin", True) if require_admin and context.context.event.role != "admin": return ( "error: Permission denied. Shell execution is only allowed for admin users. " "Tell user to set admins in `AstrBot WebUI -> Config -> General Config` by adding their user ID to the admins list if they need this feature." f"User's ID is: {context.context.event.get_sender_id()}. User's ID can be found by using /sid command." ) return None @dataclass class ExecuteShellTool(FunctionTool): name: str = "astrbot_execute_shell" description: str = "Execute a command in the shell." parameters: dict = field( default_factory=lambda: { "type": "object", "properties": { "command": { "type": "string", "description": "The bash command to execute. Equal to 'cd {working_dir} && {your_command}'.", }, "background": { "type": "boolean", "description": "Whether to run the command in the background.", "default": False, }, "env": { "type": "object", "description": "Optional environment variables to set for the file creation process.", "additionalProperties": {"type": "string"}, "default": {}, }, }, "required": ["command"], } ) is_local: bool = False async def call( self, context: ContextWrapper[AstrAgentContext], command: str, background: bool = False, env: dict = {}, ) -> ToolExecResult: if permission_error := _check_admin_permission(context): return permission_error if self.is_local: sb = get_local_booter() else: sb = await get_booter( context.context.context, context.context.event.unified_msg_origin, ) try: result = await sb.shell.exec(command, background=background, env=env) return json.dumps(result) except Exception as e: return f"Error executing command: {str(e)}"