Files
metabuilder/workflow/executor/python/notification_helpers.py
johndoe6345789 3d6ae4cbf7 feat: Add complete Python workflow executor from AutoMetabuilder
Add full Python workflow execution engine with:

Core Executor:
- engine.py: WorkflowEngine for running n8n configs
- n8n_executor.py: N8N-style workflow execution with connections
- node_executor.py: Individual node execution with plugin dispatch
- loop_executor.py: Loop node execution with iteration control
- execution_order.py: Topological sort for node ordering

Schema & Validation:
- n8n_schema.py: N8N workflow schema types and validation
- n8n_converter.py: Legacy to n8n schema conversion

Plugin System:
- plugin_loader.py: Dynamic plugin loading
- plugin_registry.py: Plugin discovery and registration
- plugin_map.json: 116 plugin type mappings

Runtime & Context:
- runtime.py: Workflow runtime container
- input_resolver.py: Binding and coercion resolution
- value_helpers.py: Value normalization helpers
- workflow_context_builder.py: Runtime context assembly
- workflow_config_loader.py: Configuration loading
- workflow_engine_builder.py: Engine assembly with dependencies

Utilities:
- tool_calls_handler.py: LLM tool call handling
- tool_runner.py: Tool execution with logging
- notification_helpers.py: Slack/Discord notifications
- workflow_adapter.py: N8N format handling
- workflow_graph.py: Node/edge graph for visualization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:42:30 +00:00

74 lines
2.4 KiB
Python

"""Notification helpers for workflow plugins."""
import os
import logging
import asyncio
logger = logging.getLogger("autometabuilder.notifications")
def send_slack_notification(runtime, message: str):
"""Send a notification to Slack using client from runtime context."""
client = runtime.context.get("slack_client") if runtime else None
channel = os.environ.get("SLACK_CHANNEL")
if not client:
logger.warning("Slack notification skipped: Slack client not initialized.")
return
if not channel:
logger.warning("Slack notification skipped: SLACK_CHANNEL missing.")
return
try:
from slack_sdk.errors import SlackApiError
client.chat_postMessage(channel=channel, text=message)
logger.info("Slack notification sent successfully.")
except SlackApiError as e:
logger.error(f"Error sending Slack notification: {e}")
async def send_discord_notification_async(message: str, token: str, intents, channel_id: str):
"""Send Discord notification asynchronously."""
import discord
client = discord.Client(intents=intents)
@client.event
async def on_ready():
channel = client.get_channel(int(channel_id))
if channel:
await channel.send(message)
logger.info("Discord notification sent successfully.")
await client.close()
try:
await client.start(token)
except Exception as e:
logger.error(f"Error sending Discord notification: {e}")
def send_discord_notification(runtime, message: str):
"""Send a Discord notification using config from runtime context."""
token = runtime.context.get("discord_token") if runtime else None
intents = runtime.context.get("discord_intents") if runtime else None
channel_id = os.environ.get("DISCORD_CHANNEL_ID")
if not token:
logger.warning("Discord notification skipped: Discord client not initialized.")
return
if not channel_id:
logger.warning("Discord notification skipped: DISCORD_CHANNEL_ID missing.")
return
try:
asyncio.run(send_discord_notification_async(message, token, intents, channel_id))
except Exception as e:
logger.error(f"Error running Discord notification: {e}")
def notify_all(runtime, message: str):
"""Send notification to all configured channels."""
send_slack_notification(runtime, message)
send_discord_notification(runtime, message)