Initial commit: MCP Hub Community Edition v3.0.0
Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
579
plugins/appwrite/handlers/messaging.py
Normal file
579
plugins/appwrite/handlers/messaging.py
Normal file
@@ -0,0 +1,579 @@
|
||||
"""
|
||||
Messaging Handler - manages Appwrite messaging (Email, SMS, Push notifications)
|
||||
|
||||
Phase I.4: 12 tools
|
||||
- Topics: 4 (list, get, create, delete)
|
||||
- Subscribers: 2 (create, delete)
|
||||
- Messages: 6 (list, get, send_email, send_sms, send_push, delete)
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from plugins.appwrite.client import AppwriteClient
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator (12 tools)"""
|
||||
return [
|
||||
# =====================
|
||||
# TOPICS (4)
|
||||
# =====================
|
||||
{
|
||||
"name": "list_topics",
|
||||
"method_name": "list_topics",
|
||||
"description": "List all messaging topics. Topics are groups for sending messages to multiple subscribers.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"queries": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Query strings for filtering",
|
||||
},
|
||||
"search": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Search term",
|
||||
},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "get_topic",
|
||||
"method_name": "get_topic",
|
||||
"description": "Get topic details by ID.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"topic_id": {"type": "string", "description": "Topic ID"}},
|
||||
"required": ["topic_id"],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "create_topic",
|
||||
"method_name": "create_topic",
|
||||
"description": "Create a new messaging topic.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"topic_id": {
|
||||
"type": "string",
|
||||
"description": "Unique topic ID. Use 'unique()' for auto-generation",
|
||||
},
|
||||
"name": {"type": "string", "description": "Topic name"},
|
||||
"subscribe": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Permissions for who can subscribe (e.g., ['users', 'any'])",
|
||||
},
|
||||
},
|
||||
"required": ["topic_id", "name"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "delete_topic",
|
||||
"method_name": "delete_topic",
|
||||
"description": "Delete a messaging topic.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"topic_id": {"type": "string", "description": "Topic ID to delete"}},
|
||||
"required": ["topic_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
# =====================
|
||||
# SUBSCRIBERS (2)
|
||||
# =====================
|
||||
{
|
||||
"name": "create_subscriber",
|
||||
"method_name": "create_subscriber",
|
||||
"description": "Add a subscriber to a topic.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"topic_id": {"type": "string", "description": "Topic ID"},
|
||||
"subscriber_id": {
|
||||
"type": "string",
|
||||
"description": "Unique subscriber ID. Use 'unique()' for auto-generation",
|
||||
},
|
||||
"target_id": {
|
||||
"type": "string",
|
||||
"description": "Target ID (user's target for messaging)",
|
||||
},
|
||||
},
|
||||
"required": ["topic_id", "subscriber_id", "target_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "delete_subscriber",
|
||||
"method_name": "delete_subscriber",
|
||||
"description": "Remove a subscriber from a topic.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"topic_id": {"type": "string", "description": "Topic ID"},
|
||||
"subscriber_id": {"type": "string", "description": "Subscriber ID to remove"},
|
||||
},
|
||||
"required": ["topic_id", "subscriber_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
# =====================
|
||||
# MESSAGES (6)
|
||||
# =====================
|
||||
{
|
||||
"name": "list_messages",
|
||||
"method_name": "list_messages",
|
||||
"description": "List all messages (sent and draft).",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"queries": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Query strings for filtering",
|
||||
},
|
||||
"search": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Search term",
|
||||
},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "get_message",
|
||||
"method_name": "get_message",
|
||||
"description": "Get message details by ID.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"message_id": {"type": "string", "description": "Message ID"}},
|
||||
"required": ["message_id"],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "send_email",
|
||||
"method_name": "send_email",
|
||||
"description": "Send an email message. Requires configured email provider.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"description": "Unique message ID. Use 'unique()' for auto-generation",
|
||||
},
|
||||
"subject": {"type": "string", "description": "Email subject"},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Email content (HTML supported if html=true)",
|
||||
},
|
||||
"topics": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Topic IDs to send to",
|
||||
},
|
||||
"users": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "User IDs to send to",
|
||||
},
|
||||
"targets": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Target IDs to send to",
|
||||
},
|
||||
"cc": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "CC recipients",
|
||||
},
|
||||
"bcc": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "BCC recipients",
|
||||
},
|
||||
"html": {
|
||||
"type": "boolean",
|
||||
"description": "Send as HTML email",
|
||||
"default": True,
|
||||
},
|
||||
"draft": {
|
||||
"type": "boolean",
|
||||
"description": "Save as draft instead of sending",
|
||||
"default": False,
|
||||
},
|
||||
"scheduled_at": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Schedule send time (ISO 8601 format)",
|
||||
},
|
||||
},
|
||||
"required": ["message_id", "subject", "content"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "send_sms",
|
||||
"method_name": "send_sms",
|
||||
"description": "Send an SMS message. Requires configured SMS provider.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"description": "Unique message ID. Use 'unique()' for auto-generation",
|
||||
},
|
||||
"content": {"type": "string", "description": "SMS content"},
|
||||
"topics": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Topic IDs to send to",
|
||||
},
|
||||
"users": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "User IDs to send to",
|
||||
},
|
||||
"targets": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Target IDs to send to",
|
||||
},
|
||||
"draft": {"type": "boolean", "description": "Save as draft", "default": False},
|
||||
"scheduled_at": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Schedule send time (ISO 8601 format)",
|
||||
},
|
||||
},
|
||||
"required": ["message_id", "content"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "send_push",
|
||||
"method_name": "send_push",
|
||||
"description": "Send a push notification. Requires configured push provider (FCM/APNs).",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"description": "Unique message ID. Use 'unique()' for auto-generation",
|
||||
},
|
||||
"title": {"type": "string", "description": "Notification title"},
|
||||
"body": {"type": "string", "description": "Notification body"},
|
||||
"topics": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Topic IDs to send to",
|
||||
},
|
||||
"users": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "User IDs to send to",
|
||||
},
|
||||
"targets": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Target IDs to send to",
|
||||
},
|
||||
"data": {
|
||||
"anyOf": [{"type": "object"}, {"type": "null"}],
|
||||
"description": "Custom data payload",
|
||||
},
|
||||
"action": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Action URL",
|
||||
},
|
||||
"image": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Image URL",
|
||||
},
|
||||
"icon": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Icon URL",
|
||||
},
|
||||
"sound": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Sound name",
|
||||
},
|
||||
"badge": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||
"description": "Badge number (iOS)",
|
||||
},
|
||||
"draft": {"type": "boolean", "description": "Save as draft", "default": False},
|
||||
"scheduled_at": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Schedule send time (ISO 8601 format)",
|
||||
},
|
||||
},
|
||||
"required": ["message_id", "title", "body"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "delete_message",
|
||||
"method_name": "delete_message",
|
||||
"description": "Delete a message.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message_id": {"type": "string", "description": "Message ID to delete"}
|
||||
},
|
||||
"required": ["message_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
]
|
||||
|
||||
# =====================
|
||||
# HANDLER FUNCTIONS
|
||||
# =====================
|
||||
|
||||
async def list_topics(
|
||||
client: AppwriteClient, queries: list[str] | None = None, search: str | None = None
|
||||
) -> str:
|
||||
"""List all topics."""
|
||||
try:
|
||||
result = await client.list_topics(queries=queries, search=search)
|
||||
topics = result.get("topics", [])
|
||||
|
||||
response = {"success": True, "total": result.get("total", len(topics)), "topics": topics}
|
||||
return json.dumps(response, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def get_topic(client: AppwriteClient, topic_id: str) -> str:
|
||||
"""Get topic by ID."""
|
||||
try:
|
||||
result = await client.get_topic(topic_id)
|
||||
return json.dumps({"success": True, "topic": result}, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def create_topic(
|
||||
client: AppwriteClient, topic_id: str, name: str, subscribe: list[str] | None = None
|
||||
) -> str:
|
||||
"""Create a new topic."""
|
||||
try:
|
||||
result = await client.create_topic(topic_id=topic_id, name=name, subscribe=subscribe)
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"Topic '{name}' created successfully", "topic": result},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def delete_topic(client: AppwriteClient, topic_id: str) -> str:
|
||||
"""Delete topic."""
|
||||
try:
|
||||
await client.delete_topic(topic_id)
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"Topic '{topic_id}' deleted successfully"}, indent=2
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def create_subscriber(
|
||||
client: AppwriteClient, topic_id: str, subscriber_id: str, target_id: str
|
||||
) -> str:
|
||||
"""Add subscriber to topic."""
|
||||
try:
|
||||
result = await client.create_subscriber(
|
||||
topic_id=topic_id, subscriber_id=subscriber_id, target_id=target_id
|
||||
)
|
||||
return json.dumps(
|
||||
{"success": True, "message": "Subscriber added to topic", "subscriber": result},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def delete_subscriber(client: AppwriteClient, topic_id: str, subscriber_id: str) -> str:
|
||||
"""Remove subscriber from topic."""
|
||||
try:
|
||||
await client.delete_subscriber(topic_id, subscriber_id)
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"Subscriber '{subscriber_id}' removed from topic"},
|
||||
indent=2,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def list_messages(
|
||||
client: AppwriteClient, queries: list[str] | None = None, search: str | None = None
|
||||
) -> str:
|
||||
"""List all messages."""
|
||||
try:
|
||||
result = await client.list_messages(queries=queries, search=search)
|
||||
messages = result.get("messages", [])
|
||||
|
||||
response = {
|
||||
"success": True,
|
||||
"total": result.get("total", len(messages)),
|
||||
"messages": messages,
|
||||
}
|
||||
return json.dumps(response, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def get_message(client: AppwriteClient, message_id: str) -> str:
|
||||
"""Get message by ID."""
|
||||
try:
|
||||
result = await client.get_message(message_id)
|
||||
return json.dumps({"success": True, "message": result}, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def send_email(
|
||||
client: AppwriteClient,
|
||||
message_id: str,
|
||||
subject: str,
|
||||
content: str,
|
||||
topics: list[str] | None = None,
|
||||
users: list[str] | None = None,
|
||||
targets: list[str] | None = None,
|
||||
cc: list[str] | None = None,
|
||||
bcc: list[str] | None = None,
|
||||
html: bool = True,
|
||||
draft: bool = False,
|
||||
scheduled_at: str | None = None,
|
||||
) -> str:
|
||||
"""Send email message."""
|
||||
try:
|
||||
result = await client.create_email(
|
||||
message_id=message_id,
|
||||
subject=subject,
|
||||
content=content,
|
||||
topics=topics,
|
||||
users=users,
|
||||
targets=targets,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
html=html,
|
||||
draft=draft,
|
||||
scheduled_at=scheduled_at,
|
||||
)
|
||||
|
||||
action = "saved as draft" if draft else "sent"
|
||||
if scheduled_at:
|
||||
action = f"scheduled for {scheduled_at}"
|
||||
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"Email {action} successfully", "email": result},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "provider" in error_msg.lower():
|
||||
error_msg += " (Hint: Make sure you have configured an email provider in Appwrite)"
|
||||
return json.dumps({"success": False, "error": error_msg}, indent=2)
|
||||
|
||||
async def send_sms(
|
||||
client: AppwriteClient,
|
||||
message_id: str,
|
||||
content: str,
|
||||
topics: list[str] | None = None,
|
||||
users: list[str] | None = None,
|
||||
targets: list[str] | None = None,
|
||||
draft: bool = False,
|
||||
scheduled_at: str | None = None,
|
||||
) -> str:
|
||||
"""Send SMS message."""
|
||||
try:
|
||||
result = await client.create_sms(
|
||||
message_id=message_id,
|
||||
content=content,
|
||||
topics=topics,
|
||||
users=users,
|
||||
targets=targets,
|
||||
draft=draft,
|
||||
scheduled_at=scheduled_at,
|
||||
)
|
||||
|
||||
action = "saved as draft" if draft else "sent"
|
||||
if scheduled_at:
|
||||
action = f"scheduled for {scheduled_at}"
|
||||
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"SMS {action} successfully", "sms": result},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "provider" in error_msg.lower():
|
||||
error_msg += " (Hint: Make sure you have configured an SMS provider in Appwrite)"
|
||||
return json.dumps({"success": False, "error": error_msg}, indent=2)
|
||||
|
||||
async def send_push(
|
||||
client: AppwriteClient,
|
||||
message_id: str,
|
||||
title: str,
|
||||
body: str,
|
||||
topics: list[str] | None = None,
|
||||
users: list[str] | None = None,
|
||||
targets: list[str] | None = None,
|
||||
data: dict[str, Any] | None = None,
|
||||
action: str | None = None,
|
||||
image: str | None = None,
|
||||
icon: str | None = None,
|
||||
sound: str | None = None,
|
||||
badge: int | None = None,
|
||||
draft: bool = False,
|
||||
scheduled_at: str | None = None,
|
||||
) -> str:
|
||||
"""Send push notification."""
|
||||
try:
|
||||
result = await client.create_push(
|
||||
message_id=message_id,
|
||||
title=title,
|
||||
body=body,
|
||||
topics=topics,
|
||||
users=users,
|
||||
targets=targets,
|
||||
data_payload=data,
|
||||
action=action,
|
||||
image=image,
|
||||
icon=icon,
|
||||
sound=sound,
|
||||
badge=badge,
|
||||
draft=draft,
|
||||
scheduled_at=scheduled_at,
|
||||
)
|
||||
|
||||
action_text = "saved as draft" if draft else "sent"
|
||||
if scheduled_at:
|
||||
action_text = f"scheduled for {scheduled_at}"
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"message": f"Push notification {action_text} successfully",
|
||||
"push": result,
|
||||
},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "provider" in error_msg.lower():
|
||||
error_msg += (
|
||||
" (Hint: Make sure you have configured a push provider (FCM/APNs) in Appwrite)"
|
||||
)
|
||||
return json.dumps({"success": False, "error": error_msg}, indent=2)
|
||||
|
||||
async def delete_message(client: AppwriteClient, message_id: str) -> str:
|
||||
"""Delete message."""
|
||||
try:
|
||||
await client.delete_message(message_id)
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"Message '{message_id}' deleted successfully"}, indent=2
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
Reference in New Issue
Block a user