mirror of
https://github.com/johndoe6345789/AutoMetabuilder.git
synced 2026-04-24 13:54:59 +00:00
Make dependencies plugins - refactor notifications to use backend clients
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
0
backend/autometabuilder/integrations/__init__.py
Normal file
0
backend/autometabuilder/integrations/__init__.py
Normal file
55
backend/autometabuilder/integrations/notifications.py
Normal file
55
backend/autometabuilder/integrations/notifications.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import os
|
||||
import logging
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
import discord
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger("autometabuilder.notifications")
|
||||
|
||||
def send_slack_notification(message: str):
|
||||
token = os.environ.get("SLACK_BOT_TOKEN")
|
||||
channel = os.environ.get("SLACK_CHANNEL")
|
||||
if not token or not channel:
|
||||
logger.warning("Slack notification skipped: SLACK_BOT_TOKEN or SLACK_CHANNEL missing.")
|
||||
return
|
||||
|
||||
client = WebClient(token=token)
|
||||
try:
|
||||
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 = os.environ.get("DISCORD_BOT_TOKEN")
|
||||
channel_id = os.environ.get("DISCORD_CHANNEL_ID")
|
||||
if not token or not channel_id:
|
||||
logger.warning("Discord notification skipped: DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID missing.")
|
||||
return
|
||||
|
||||
intents = discord.Intents.default()
|
||||
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(message: str):
|
||||
try:
|
||||
asyncio.run(send_discord_notification_async(message))
|
||||
except Exception as e:
|
||||
logger.error(f"Error running Discord notification: {e}")
|
||||
|
||||
def notify_all(message: str):
|
||||
send_slack_notification(message)
|
||||
send_discord_notification(message)
|
||||
@@ -1,39 +1,36 @@
|
||||
"""Notification helpers for workflow plugins."""
|
||||
import os
|
||||
import logging
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
import discord
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger("autometabuilder.notifications")
|
||||
|
||||
|
||||
def send_slack_notification(message: str):
|
||||
"""Send a notification to Slack."""
|
||||
token = os.environ.get("SLACK_BOT_TOKEN")
|
||||
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 token or not channel:
|
||||
logger.warning("Slack notification skipped: SLACK_BOT_TOKEN or SLACK_CHANNEL missing.")
|
||||
|
||||
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
|
||||
|
||||
client = WebClient(token=token)
|
||||
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):
|
||||
async def send_discord_notification_async(message: str, token: str, intents, channel_id: str):
|
||||
"""Send Discord notification asynchronously."""
|
||||
token = os.environ.get("DISCORD_BOT_TOKEN")
|
||||
channel_id = os.environ.get("DISCORD_CHANNEL_ID")
|
||||
if not token or not channel_id:
|
||||
logger.warning("Discord notification skipped: DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID missing.")
|
||||
return
|
||||
|
||||
intents = discord.Intents.default()
|
||||
import discord
|
||||
|
||||
client = discord.Client(intents=intents)
|
||||
|
||||
@client.event
|
||||
@@ -50,15 +47,27 @@ async def send_discord_notification_async(message: str):
|
||||
logger.error(f"Error sending Discord notification: {e}")
|
||||
|
||||
|
||||
def send_discord_notification(message: str):
|
||||
"""Send a Discord notification."""
|
||||
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))
|
||||
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(message: str):
|
||||
def notify_all(runtime, message: str):
|
||||
"""Send notification to all configured channels."""
|
||||
send_slack_notification(message)
|
||||
send_discord_notification(message)
|
||||
send_slack_notification(runtime, message)
|
||||
send_discord_notification(runtime, message)
|
||||
|
||||
@@ -5,7 +5,7 @@ This document describes all available workflow plugins for building declarative
|
||||
## Directory Structure
|
||||
|
||||
Plugins are now organized into subdirectories by category:
|
||||
- **backend/** - Backend infrastructure and initialization plugins (12 plugins)
|
||||
- **backend/** - Backend infrastructure and initialization plugins (14 plugins)
|
||||
- **core/** - Core workflow orchestration plugins (7 plugins)
|
||||
- **tools/** - Tool execution and development plugins (7 plugins)
|
||||
- **notifications/** - External notification integrations (3 plugins)
|
||||
@@ -21,7 +21,7 @@ Plugins are now organized into subdirectories by category:
|
||||
- **utils/** - Utility functions (7 plugins)
|
||||
- **web/** - Web UI and Flask operations (26 plugins)
|
||||
|
||||
**Total: 93 plugins**
|
||||
**Total: 95 plugins**
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -178,12 +178,16 @@ Run command inside Docker container.
|
||||
|
||||
## Notification Plugins
|
||||
|
||||
**Note:** Notification plugins require the corresponding backend plugins (`backend.create_slack` and/or `backend.create_discord`) to be run first to initialize the clients.
|
||||
|
||||
### `notifications.slack`
|
||||
Send notification to Slack.
|
||||
|
||||
**Prerequisites:**
|
||||
- `backend.create_slack` must be run first to initialize the Slack client
|
||||
|
||||
**Inputs:**
|
||||
- `message` - The message to send
|
||||
- `token` - Optional Slack bot token (defaults to SLACK_BOT_TOKEN env var)
|
||||
- `channel` - Optional channel (defaults to SLACK_CHANNEL env var)
|
||||
|
||||
**Outputs:**
|
||||
@@ -195,9 +199,11 @@ Send notification to Slack.
|
||||
### `notifications.discord`
|
||||
Send notification to Discord.
|
||||
|
||||
**Prerequisites:**
|
||||
- `backend.create_discord` must be run first to initialize the Discord configuration
|
||||
|
||||
**Inputs:**
|
||||
- `message` - The message to send
|
||||
- `token` - Optional Discord bot token (defaults to DISCORD_BOT_TOKEN env var)
|
||||
- `channel_id` - Optional channel ID (defaults to DISCORD_CHANNEL_ID env var)
|
||||
|
||||
**Outputs:**
|
||||
@@ -209,6 +215,9 @@ Send notification to Discord.
|
||||
### `notifications.all`
|
||||
Send notification to all configured channels (Slack and Discord).
|
||||
|
||||
**Prerequisites:**
|
||||
- `backend.create_slack` and `backend.create_discord` should be run first for full functionality
|
||||
|
||||
**Inputs:**
|
||||
- `message` - The message to send to all channels
|
||||
|
||||
@@ -858,6 +867,28 @@ Initialize OpenAI client.
|
||||
- `result` - OpenAI client
|
||||
- `initialized` - Boolean
|
||||
|
||||
### `backend.create_slack`
|
||||
Initialize Slack WebClient.
|
||||
|
||||
**Inputs:**
|
||||
- `token` - Optional Slack bot token (defaults to SLACK_BOT_TOKEN env var)
|
||||
|
||||
**Outputs:**
|
||||
- `result` - Slack client
|
||||
- `initialized` - Boolean
|
||||
- `error` - Error message (if failed)
|
||||
|
||||
### `backend.create_discord`
|
||||
Initialize Discord client configuration.
|
||||
|
||||
**Inputs:**
|
||||
- `token` - Optional Discord bot token (defaults to DISCORD_BOT_TOKEN env var)
|
||||
|
||||
**Outputs:**
|
||||
- `result` - Discord token
|
||||
- `initialized` - Boolean
|
||||
- `error` - Error message (if failed)
|
||||
|
||||
### `backend.load_metadata`
|
||||
Load metadata.json.
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Workflow plugin: create Discord client."""
|
||||
import os
|
||||
import logging
|
||||
import discord
|
||||
|
||||
logger = logging.getLogger("autometabuilder")
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""
|
||||
Initialize Discord Client (without starting the connection).
|
||||
|
||||
Note: Discord client needs to be started asynchronously. This plugin
|
||||
just stores the token and intents configuration for later use by
|
||||
notification plugins.
|
||||
|
||||
Inputs:
|
||||
token: Optional Discord bot token (defaults to DISCORD_BOT_TOKEN env var)
|
||||
|
||||
Returns:
|
||||
dict: Contains initialization status and configuration
|
||||
"""
|
||||
token = inputs.get("token") or os.environ.get("DISCORD_BOT_TOKEN")
|
||||
|
||||
if not token:
|
||||
logger.warning("Discord client not initialized: DISCORD_BOT_TOKEN missing.")
|
||||
runtime.context["discord_token"] = None
|
||||
return {"result": None, "initialized": False, "error": "DISCORD_BOT_TOKEN missing"}
|
||||
|
||||
# Store token and intents configuration in context
|
||||
# Discord client must be created per-use due to its async nature
|
||||
runtime.context["discord_token"] = token
|
||||
runtime.context["discord_intents"] = discord.Intents.default()
|
||||
|
||||
logger.info("Discord configuration initialized successfully.")
|
||||
return {"result": token, "initialized": True}
|
||||
@@ -0,0 +1,33 @@
|
||||
"""Workflow plugin: create Slack client."""
|
||||
import os
|
||||
import logging
|
||||
from slack_sdk import WebClient
|
||||
|
||||
logger = logging.getLogger("autometabuilder")
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""
|
||||
Initialize Slack WebClient.
|
||||
|
||||
Inputs:
|
||||
token: Optional Slack bot token (defaults to SLACK_BOT_TOKEN env var)
|
||||
|
||||
Returns:
|
||||
dict: Contains the Slack client in result and initialized status
|
||||
"""
|
||||
token = inputs.get("token") or os.environ.get("SLACK_BOT_TOKEN")
|
||||
|
||||
if not token:
|
||||
logger.warning("Slack client not initialized: SLACK_BOT_TOKEN missing.")
|
||||
runtime.context["slack_client"] = None
|
||||
return {"result": None, "initialized": False, "error": "SLACK_BOT_TOKEN missing"}
|
||||
|
||||
# Create Slack client
|
||||
client = WebClient(token=token)
|
||||
|
||||
# Store in context for other plugins to use
|
||||
runtime.context["slack_client"] = client
|
||||
|
||||
logger.info("Slack client initialized successfully.")
|
||||
return {"result": client, "initialized": True}
|
||||
@@ -47,6 +47,6 @@ def run(runtime, inputs):
|
||||
|
||||
if runtime.context["args"].yolo and _is_mvp_reached():
|
||||
runtime.logger.info("MVP reached. Stopping YOLO loop.")
|
||||
notify_all("AutoMetabuilder YOLO loop stopped: MVP reached.")
|
||||
notify_all(runtime, "AutoMetabuilder YOLO loop stopped: MVP reached.")
|
||||
|
||||
return {"messages": messages}
|
||||
|
||||
@@ -19,7 +19,7 @@ def run(runtime, inputs):
|
||||
runtime.logger
|
||||
)
|
||||
if not tool_calls and resp_msg.content:
|
||||
notify_all(f"AutoMetabuilder task complete: {resp_msg.content[:100]}...")
|
||||
notify_all(runtime, f"AutoMetabuilder task complete: {resp_msg.content[:100]}...")
|
||||
return {
|
||||
"tool_results": tool_results,
|
||||
"no_tool_calls": not bool(tool_calls)
|
||||
|
||||
@@ -1,39 +1,29 @@
|
||||
"""Workflow plugin: send notification to all channels."""
|
||||
import os
|
||||
import logging
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
import discord
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger("autometabuilder.notifications")
|
||||
|
||||
|
||||
def _send_slack(message: str):
|
||||
"""Send Slack notification."""
|
||||
token = os.environ.get("SLACK_BOT_TOKEN")
|
||||
channel = os.environ.get("SLACK_CHANNEL")
|
||||
if not token or not channel:
|
||||
logger.warning("Slack notification skipped: SLACK_BOT_TOKEN or SLACK_CHANNEL missing.")
|
||||
def _send_slack(client, message: str, channel: str):
|
||||
"""Send Slack notification using provided client."""
|
||||
if not client or not channel:
|
||||
logger.warning("Slack notification skipped: client or channel missing.")
|
||||
return
|
||||
|
||||
client = WebClient(token=token)
|
||||
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_async(message: str):
|
||||
async def _send_discord_async(message: str, token: str, intents, channel_id: str):
|
||||
"""Send Discord notification asynchronously."""
|
||||
token = os.environ.get("DISCORD_BOT_TOKEN")
|
||||
channel_id = os.environ.get("DISCORD_CHANNEL_ID")
|
||||
if not token or not channel_id:
|
||||
logger.warning("Discord notification skipped: DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID missing.")
|
||||
return
|
||||
import discord
|
||||
|
||||
intents = discord.Intents.default()
|
||||
client = discord.Client(intents=intents)
|
||||
|
||||
@client.event
|
||||
@@ -50,10 +40,14 @@ async def _send_discord_async(message: str):
|
||||
logger.error(f"Error sending Discord notification: {e}")
|
||||
|
||||
|
||||
def _send_discord(message: str):
|
||||
def _send_discord(message: str, token: str, intents, channel_id: str):
|
||||
"""Send Discord notification."""
|
||||
if not token or not channel_id:
|
||||
logger.warning("Discord notification skipped: token or channel_id missing.")
|
||||
return
|
||||
|
||||
try:
|
||||
asyncio.run(_send_discord_async(message))
|
||||
asyncio.run(_send_discord_async(message, token, intents, channel_id))
|
||||
except Exception as e:
|
||||
logger.error(f"Error running Discord notification: {e}")
|
||||
|
||||
@@ -70,9 +64,18 @@ def run(runtime, inputs):
|
||||
"""
|
||||
message = inputs.get("message", "")
|
||||
|
||||
# Get Slack client from runtime context
|
||||
slack_client = runtime.context.get("slack_client")
|
||||
slack_channel = os.environ.get("SLACK_CHANNEL")
|
||||
|
||||
# Get Discord config from runtime context
|
||||
discord_token = runtime.context.get("discord_token")
|
||||
discord_intents = runtime.context.get("discord_intents")
|
||||
discord_channel_id = os.environ.get("DISCORD_CHANNEL_ID")
|
||||
|
||||
# Send to both channels
|
||||
_send_slack(message)
|
||||
_send_discord(message)
|
||||
_send_slack(slack_client, message, slack_channel)
|
||||
_send_discord(message, discord_token, discord_intents, discord_channel_id)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
"""Workflow plugin: send Discord notification."""
|
||||
import os
|
||||
import logging
|
||||
import discord
|
||||
import asyncio
|
||||
|
||||
logger = logging.getLogger("autometabuilder.notifications")
|
||||
|
||||
|
||||
async def _send_discord_notification_async(message: str, token: str, channel_id: str):
|
||||
async def _send_discord_notification_async(message: str, token: str, intents, channel_id: str):
|
||||
"""Send Discord notification asynchronously."""
|
||||
intents = discord.Intents.default()
|
||||
# Import discord here to avoid loading it at module level
|
||||
import discord
|
||||
|
||||
client = discord.Client(intents=intents)
|
||||
|
||||
@client.event
|
||||
@@ -33,26 +34,36 @@ def run(runtime, inputs):
|
||||
|
||||
Inputs:
|
||||
message: The message to send
|
||||
token: Optional Discord bot token (defaults to DISCORD_BOT_TOKEN env var)
|
||||
channel_id: Optional channel ID (defaults to DISCORD_CHANNEL_ID env var)
|
||||
|
||||
Returns:
|
||||
dict: Contains success status and any error message
|
||||
"""
|
||||
message = inputs.get("message", "")
|
||||
token = inputs.get("token") or os.environ.get("DISCORD_BOT_TOKEN")
|
||||
channel_id = inputs.get("channel_id") or os.environ.get("DISCORD_CHANNEL_ID")
|
||||
|
||||
if not token or not channel_id:
|
||||
logger.warning("Discord notification skipped: DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID missing.")
|
||||
# Get Discord token and intents from runtime context (initialized by backend.create_discord)
|
||||
token = runtime.context.get("discord_token")
|
||||
intents = runtime.context.get("discord_intents")
|
||||
|
||||
if not token:
|
||||
logger.warning("Discord notification skipped: Discord client not initialized.")
|
||||
return {
|
||||
"success": False,
|
||||
"skipped": True,
|
||||
"error": "DISCORD_BOT_TOKEN or DISCORD_CHANNEL_ID missing"
|
||||
"error": "Discord client not initialized"
|
||||
}
|
||||
|
||||
if not channel_id:
|
||||
logger.warning("Discord notification skipped: DISCORD_CHANNEL_ID missing.")
|
||||
return {
|
||||
"success": False,
|
||||
"skipped": True,
|
||||
"error": "DISCORD_CHANNEL_ID missing"
|
||||
}
|
||||
|
||||
try:
|
||||
asyncio.run(_send_discord_notification_async(message, token, channel_id))
|
||||
asyncio.run(_send_discord_notification_async(message, token, intents, channel_id))
|
||||
return {"success": True, "message": "Discord notification sent"}
|
||||
except Exception as e:
|
||||
logger.error(f"Error running Discord notification: {e}")
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""Workflow plugin: send Slack notification."""
|
||||
import os
|
||||
import logging
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
|
||||
logger = logging.getLogger("autometabuilder.notifications")
|
||||
|
||||
@@ -13,26 +11,36 @@ def run(runtime, inputs):
|
||||
|
||||
Inputs:
|
||||
message: The message to send
|
||||
token: Optional Slack bot token (defaults to SLACK_BOT_TOKEN env var)
|
||||
channel: Optional channel (defaults to SLACK_CHANNEL env var)
|
||||
|
||||
Returns:
|
||||
dict: Contains success status and any error message
|
||||
"""
|
||||
message = inputs.get("message", "")
|
||||
token = inputs.get("token") or os.environ.get("SLACK_BOT_TOKEN")
|
||||
channel = inputs.get("channel") or os.environ.get("SLACK_CHANNEL")
|
||||
|
||||
if not token or not channel:
|
||||
logger.warning("Slack notification skipped: SLACK_BOT_TOKEN or SLACK_CHANNEL missing.")
|
||||
# Get Slack client from runtime context (initialized by backend.create_slack)
|
||||
client = runtime.context.get("slack_client")
|
||||
|
||||
if not client:
|
||||
logger.warning("Slack notification skipped: Slack client not initialized.")
|
||||
return {
|
||||
"success": False,
|
||||
"skipped": True,
|
||||
"error": "SLACK_BOT_TOKEN or SLACK_CHANNEL missing"
|
||||
"error": "Slack client not initialized"
|
||||
}
|
||||
|
||||
if not channel:
|
||||
logger.warning("Slack notification skipped: SLACK_CHANNEL missing.")
|
||||
return {
|
||||
"success": False,
|
||||
"skipped": True,
|
||||
"error": "SLACK_CHANNEL missing"
|
||||
}
|
||||
|
||||
client = WebClient(token=token)
|
||||
try:
|
||||
# Import SlackApiError here to handle errors from the client
|
||||
from slack_sdk.errors import SlackApiError
|
||||
client.chat_postMessage(channel=channel, text=message)
|
||||
logger.info("Slack notification sent successfully.")
|
||||
return {"success": True, "message": "Slack notification sent"}
|
||||
|
||||
Reference in New Issue
Block a user