""" 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)