101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
import json
|
|
from typing import Optional, Any
|
|
import httpx
|
|
from app.nodes.base import NodeExecutor
|
|
from app.context import FlowContext
|
|
|
|
|
|
class JavaScriptExecutor(NodeExecutor):
|
|
"""Execute Python expressions with restricted globals"""
|
|
|
|
ALLOWED_BUILTINS = {
|
|
'abs': abs, 'all': all, 'any': any, 'bool': bool,
|
|
'dict': dict, 'float': float, 'int': int, 'len': len,
|
|
'list': list, 'max': max, 'min': min, 'round': round,
|
|
'str': str, 'sum': sum, 'sorted': sorted,
|
|
}
|
|
|
|
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
|
|
code = config.get("code", "")
|
|
output_variable = config.get("output_variable", "_result")
|
|
|
|
if not code:
|
|
return "default"
|
|
|
|
exec_globals = {
|
|
'__builtins__': self.ALLOWED_BUILTINS,
|
|
'context': {
|
|
'contact': context.contact,
|
|
'conversation': context.conversation,
|
|
'message': context.message,
|
|
'variables': context.variables.copy(),
|
|
},
|
|
'variables': context.variables.copy(),
|
|
}
|
|
|
|
try:
|
|
result = eval(code, exec_globals, {})
|
|
if result is not None:
|
|
context.set(output_variable, result)
|
|
return "success"
|
|
except Exception as e:
|
|
context.set("_script_error", str(e))
|
|
return "error"
|
|
|
|
|
|
class HttpRequestExecutor(NodeExecutor):
|
|
"""Make HTTP requests to external APIs"""
|
|
|
|
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
|
|
url = context.interpolate(config.get("url", ""))
|
|
method = config.get("method", "GET").upper()
|
|
headers = config.get("headers", {})
|
|
body = config.get("body")
|
|
output_variable = config.get("output_variable", "_http_response")
|
|
timeout = config.get("timeout", 10)
|
|
|
|
if not url:
|
|
return "error"
|
|
|
|
headers = {k: context.interpolate(str(v)) for k, v in headers.items()}
|
|
if body and isinstance(body, str):
|
|
body = context.interpolate(body)
|
|
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
json_body = None
|
|
if body:
|
|
try:
|
|
json_body = json.loads(body) if isinstance(body, str) else body
|
|
except json.JSONDecodeError:
|
|
json_body = None
|
|
|
|
response = await client.request(
|
|
method=method,
|
|
url=url,
|
|
headers=headers,
|
|
json=json_body,
|
|
timeout=timeout
|
|
)
|
|
|
|
response_json = None
|
|
if response.headers.get("content-type", "").startswith("application/json"):
|
|
try:
|
|
response_json = response.json()
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
context.set(output_variable, {
|
|
"status": response.status_code,
|
|
"body": response.text,
|
|
"json": response_json
|
|
})
|
|
|
|
if 200 <= response.status_code < 300:
|
|
return "success"
|
|
return "error"
|
|
|
|
except Exception as e:
|
|
context.set("_http_error", str(e))
|
|
return "error"
|