Initial commit of OpenClaw agent Chloe
This commit is contained in:
214
skills/agentmail/scripts/check_inbox.py
Normal file
214
skills/agentmail/scripts/check_inbox.py
Normal file
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check AgentMail inbox for messages
|
||||
|
||||
Usage:
|
||||
# List recent messages
|
||||
python check_inbox.py --inbox "myagent@agentmail.to"
|
||||
|
||||
# Get specific message
|
||||
python check_inbox.py --inbox "myagent@agentmail.to" --message "msg_123abc"
|
||||
|
||||
# List threads
|
||||
python check_inbox.py --inbox "myagent@agentmail.to" --threads
|
||||
|
||||
# Monitor for new messages (poll every N seconds)
|
||||
python check_inbox.py --inbox "myagent@agentmail.to" --monitor 30
|
||||
|
||||
Environment:
|
||||
AGENTMAIL_API_KEY: Your AgentMail API key
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from agentmail import AgentMail
|
||||
except ImportError:
|
||||
print("Error: agentmail package not found. Install with: pip install agentmail")
|
||||
sys.exit(1)
|
||||
|
||||
def format_timestamp(iso_string):
|
||||
"""Format ISO timestamp for display"""
|
||||
try:
|
||||
dt = datetime.fromisoformat(iso_string.replace('Z', '+00:00'))
|
||||
return dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
return iso_string
|
||||
|
||||
def print_message_summary(message):
|
||||
"""Print a summary of a message"""
|
||||
from_addr = message.get('from', [{}])[0].get('email', 'Unknown')
|
||||
from_name = message.get('from', [{}])[0].get('name', '')
|
||||
subject = message.get('subject', '(no subject)')
|
||||
timestamp = format_timestamp(message.get('timestamp', ''))
|
||||
preview = message.get('preview', message.get('text', ''))[:100]
|
||||
|
||||
print(f"📧 {message.get('message_id', 'N/A')}")
|
||||
print(f" From: {from_name} <{from_addr}>" if from_name else f" From: {from_addr}")
|
||||
print(f" Subject: {subject}")
|
||||
print(f" Time: {timestamp}")
|
||||
if preview:
|
||||
print(f" Preview: {preview}{'...' if len(preview) == 100 else ''}")
|
||||
print()
|
||||
|
||||
def print_thread_summary(thread):
|
||||
"""Print a summary of a thread"""
|
||||
subject = thread.get('subject', '(no subject)')
|
||||
participants = ', '.join(thread.get('participants', []))
|
||||
count = thread.get('message_count', 0)
|
||||
timestamp = format_timestamp(thread.get('last_message_at', ''))
|
||||
|
||||
print(f"🧵 {thread.get('thread_id', 'N/A')}")
|
||||
print(f" Subject: {subject}")
|
||||
print(f" Participants: {participants}")
|
||||
print(f" Messages: {count}")
|
||||
print(f" Last: {timestamp}")
|
||||
print()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Check AgentMail inbox')
|
||||
parser.add_argument('--inbox', required=True, help='Inbox email address')
|
||||
parser.add_argument('--message', help='Get specific message by ID')
|
||||
parser.add_argument('--threads', action='store_true', help='List threads instead of messages')
|
||||
parser.add_argument('--monitor', type=int, metavar='SECONDS', help='Monitor for new messages (poll interval)')
|
||||
parser.add_argument('--limit', type=int, default=10, help='Number of items to fetch (default: 10)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get API key
|
||||
api_key = os.getenv('AGENTMAIL_API_KEY')
|
||||
if not api_key:
|
||||
print("Error: AGENTMAIL_API_KEY environment variable not set")
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize client
|
||||
client = AgentMail(api_key=api_key)
|
||||
|
||||
if args.monitor:
|
||||
print(f"🔍 Monitoring {args.inbox} (checking every {args.monitor} seconds)")
|
||||
print("Press Ctrl+C to stop\n")
|
||||
|
||||
last_message_ids = set()
|
||||
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
messages = client.inboxes.messages.list(
|
||||
inbox_id=args.inbox,
|
||||
limit=args.limit
|
||||
)
|
||||
|
||||
new_messages = []
|
||||
current_message_ids = set()
|
||||
|
||||
for message in messages.messages:
|
||||
msg_id = message.get('message_id')
|
||||
current_message_ids.add(msg_id)
|
||||
|
||||
if msg_id not in last_message_ids:
|
||||
new_messages.append(message)
|
||||
|
||||
if new_messages:
|
||||
print(f"🆕 Found {len(new_messages)} new message(s):")
|
||||
for message in new_messages:
|
||||
print_message_summary(message)
|
||||
|
||||
last_message_ids = current_message_ids
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error checking inbox: {e}")
|
||||
|
||||
time.sleep(args.monitor)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Monitoring stopped")
|
||||
return
|
||||
|
||||
elif args.message:
|
||||
# Get specific message
|
||||
try:
|
||||
message = client.inboxes.messages.get(
|
||||
inbox_id=args.inbox,
|
||||
message_id=args.message
|
||||
)
|
||||
|
||||
print(f"📧 Message Details:")
|
||||
print(f" ID: {message.get('message_id')}")
|
||||
print(f" Thread: {message.get('thread_id')}")
|
||||
|
||||
from_addr = message.get('from', [{}])[0].get('email', 'Unknown')
|
||||
from_name = message.get('from', [{}])[0].get('name', '')
|
||||
print(f" From: {from_name} <{from_addr}>" if from_name else f" From: {from_addr}")
|
||||
|
||||
to_addrs = ', '.join([addr.get('email', '') for addr in message.get('to', [])])
|
||||
print(f" To: {to_addrs}")
|
||||
|
||||
print(f" Subject: {message.get('subject', '(no subject)')}")
|
||||
print(f" Time: {format_timestamp(message.get('timestamp', ''))}")
|
||||
|
||||
if message.get('labels'):
|
||||
print(f" Labels: {', '.join(message.get('labels'))}")
|
||||
|
||||
print("\n📝 Content:")
|
||||
if message.get('text'):
|
||||
print(message['text'])
|
||||
elif message.get('html'):
|
||||
print("(HTML content - use API to get full HTML)")
|
||||
else:
|
||||
print("(No text content)")
|
||||
|
||||
if message.get('attachments'):
|
||||
print(f"\n📎 Attachments ({len(message['attachments'])}):")
|
||||
for att in message['attachments']:
|
||||
print(f" • {att.get('filename', 'unnamed')} ({att.get('content_type', 'unknown type')})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error getting message: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.threads:
|
||||
# List threads
|
||||
try:
|
||||
threads = client.inboxes.threads.list(
|
||||
inbox_id=args.inbox,
|
||||
limit=args.limit
|
||||
)
|
||||
|
||||
if not threads.threads:
|
||||
print(f"📭 No threads found in {args.inbox}")
|
||||
return
|
||||
|
||||
print(f"🧵 Threads in {args.inbox} (showing {len(threads.threads)}):\n")
|
||||
for thread in threads.threads:
|
||||
print_thread_summary(thread)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error listing threads: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
# List recent messages
|
||||
try:
|
||||
messages = client.inboxes.messages.list(
|
||||
inbox_id=args.inbox,
|
||||
limit=args.limit
|
||||
)
|
||||
|
||||
if not messages.messages:
|
||||
print(f"📭 No messages found in {args.inbox}")
|
||||
return
|
||||
|
||||
print(f"📧 Messages in {args.inbox} (showing {len(messages.messages)}):\n")
|
||||
for message in messages.messages:
|
||||
print_message_summary(message)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error listing messages: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
114
skills/agentmail/scripts/send_email.py
Normal file
114
skills/agentmail/scripts/send_email.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Send email via AgentMail API
|
||||
|
||||
Usage:
|
||||
python send_email.py --inbox "sender@agentmail.to" --to "recipient@example.com" --subject "Hello" --text "Message body"
|
||||
|
||||
# With HTML content
|
||||
python send_email.py --inbox "sender@agentmail.to" --to "recipient@example.com" --subject "Hello" --html "<p>Message body</p>"
|
||||
|
||||
# With attachment
|
||||
python send_email.py --inbox "sender@agentmail.to" --to "recipient@example.com" --subject "Hello" --text "See attachment" --attach "/path/to/file.pdf"
|
||||
|
||||
Environment:
|
||||
AGENTMAIL_API_KEY: Your AgentMail API key
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import base64
|
||||
import mimetypes
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from agentmail import AgentMail
|
||||
except ImportError:
|
||||
print("Error: agentmail package not found. Install with: pip install agentmail")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Send email via AgentMail')
|
||||
parser.add_argument('--inbox', required=True, help='Sender inbox email address')
|
||||
parser.add_argument('--to', required=True, help='Recipient email address')
|
||||
parser.add_argument('--cc', help='CC email address(es), comma-separated')
|
||||
parser.add_argument('--bcc', help='BCC email address(es), comma-separated')
|
||||
parser.add_argument('--subject', default='', help='Email subject')
|
||||
parser.add_argument('--text', help='Plain text body')
|
||||
parser.add_argument('--html', help='HTML body')
|
||||
parser.add_argument('--attach', action='append', help='Attachment file path (can be used multiple times)')
|
||||
parser.add_argument('--reply-to', help='Reply-to email address')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get API key
|
||||
api_key = os.getenv('AGENTMAIL_API_KEY')
|
||||
if not api_key:
|
||||
print("Error: AGENTMAIL_API_KEY environment variable not set")
|
||||
sys.exit(1)
|
||||
|
||||
# Validate required content
|
||||
if not args.text and not args.html:
|
||||
print("Error: Must provide either --text or --html content")
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize client
|
||||
client = AgentMail(api_key=api_key)
|
||||
|
||||
# Prepare recipients
|
||||
recipients = [email.strip() for email in args.to.split(',')]
|
||||
cc_recipients = [email.strip() for email in args.cc.split(',')] if args.cc else None
|
||||
bcc_recipients = [email.strip() for email in args.bcc.split(',')] if args.bcc else None
|
||||
|
||||
# Prepare attachments
|
||||
attachments = []
|
||||
if args.attach:
|
||||
for file_path in args.attach:
|
||||
path = Path(file_path)
|
||||
if not path.exists():
|
||||
print(f"Error: Attachment file not found: {file_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Read and encode file
|
||||
with open(path, 'rb') as f:
|
||||
content = base64.b64encode(f.read()).decode('utf-8')
|
||||
|
||||
# Detect content type
|
||||
content_type, _ = mimetypes.guess_type(str(path))
|
||||
if not content_type:
|
||||
content_type = 'application/octet-stream'
|
||||
|
||||
attachments.append({
|
||||
'filename': path.name,
|
||||
'content': content,
|
||||
'content_type': content_type
|
||||
})
|
||||
print(f"Added attachment: {path.name} ({content_type})")
|
||||
|
||||
# Send email
|
||||
try:
|
||||
print(f"Sending email from {args.inbox} to {', '.join(recipients)}")
|
||||
|
||||
response = client.inboxes.messages.send(
|
||||
inbox_id=args.inbox,
|
||||
to=recipients,
|
||||
cc=cc_recipients,
|
||||
bcc=bcc_recipients,
|
||||
reply_to=args.reply_to,
|
||||
subject=args.subject,
|
||||
text=args.text,
|
||||
html=args.html,
|
||||
attachments=attachments if attachments else None
|
||||
)
|
||||
|
||||
print(f"✅ Email sent successfully!")
|
||||
print(f" Message ID: {response.message_id}")
|
||||
print(f" Thread ID: {response.thread_id}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to send email: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
180
skills/agentmail/scripts/setup_webhook.py
Normal file
180
skills/agentmail/scripts/setup_webhook.py
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Set up AgentMail webhook endpoint
|
||||
|
||||
Usage:
|
||||
# Create webhook
|
||||
python setup_webhook.py --url "https://myapp.com/webhook" --create
|
||||
|
||||
# List existing webhooks
|
||||
python setup_webhook.py --list
|
||||
|
||||
# Delete webhook
|
||||
python setup_webhook.py --delete "webhook_id"
|
||||
|
||||
# Test webhook with simple Flask receiver (for development)
|
||||
python setup_webhook.py --test-server
|
||||
|
||||
Environment:
|
||||
AGENTMAIL_API_KEY: Your AgentMail API key
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
try:
|
||||
from agentmail import AgentMail
|
||||
except ImportError:
|
||||
print("Error: agentmail package not found. Install with: pip install agentmail")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Manage AgentMail webhooks')
|
||||
parser.add_argument('--create', action='store_true', help='Create new webhook')
|
||||
parser.add_argument('--url', help='Webhook URL (required for --create)')
|
||||
parser.add_argument('--events', default='message.received', help='Comma-separated event types (default: message.received)')
|
||||
parser.add_argument('--inbox-filter', help='Filter to specific inbox(es), comma-separated')
|
||||
parser.add_argument('--client-id', help='Client ID for idempotency')
|
||||
parser.add_argument('--list', action='store_true', help='List existing webhooks')
|
||||
parser.add_argument('--delete', metavar='WEBHOOK_ID', help='Delete webhook by ID')
|
||||
parser.add_argument('--test-server', action='store_true', help='Start test webhook receiver')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.test_server:
|
||||
start_test_server()
|
||||
return
|
||||
|
||||
# Get API key
|
||||
api_key = os.getenv('AGENTMAIL_API_KEY')
|
||||
if not api_key:
|
||||
print("Error: AGENTMAIL_API_KEY environment variable not set")
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize client
|
||||
client = AgentMail(api_key=api_key)
|
||||
|
||||
if args.create:
|
||||
if not args.url:
|
||||
print("Error: --url is required when creating webhook")
|
||||
sys.exit(1)
|
||||
|
||||
# Prepare event types
|
||||
event_types = [event.strip() for event in args.events.split(',')]
|
||||
|
||||
# Prepare inbox filter
|
||||
inbox_ids = None
|
||||
if args.inbox_filter:
|
||||
inbox_ids = [inbox.strip() for inbox in args.inbox_filter.split(',')]
|
||||
|
||||
try:
|
||||
webhook = client.webhooks.create(
|
||||
url=args.url,
|
||||
event_types=event_types,
|
||||
inbox_ids=inbox_ids,
|
||||
client_id=args.client_id
|
||||
)
|
||||
|
||||
print(f"✅ Webhook created successfully!")
|
||||
print(f" ID: {webhook.webhook_id}")
|
||||
print(f" URL: {webhook.url}")
|
||||
print(f" Events: {', '.join(webhook.event_types)}")
|
||||
print(f" Enabled: {webhook.enabled}")
|
||||
if webhook.inbox_ids:
|
||||
print(f" Inboxes: {', '.join(webhook.inbox_ids)}")
|
||||
print(f" Created: {webhook.created_at}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to create webhook: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.list:
|
||||
try:
|
||||
webhooks = client.webhooks.list()
|
||||
|
||||
if not webhooks.webhooks:
|
||||
print("📭 No webhooks found")
|
||||
return
|
||||
|
||||
print(f"🪝 Webhooks ({len(webhooks.webhooks)}):\n")
|
||||
for webhook in webhooks.webhooks:
|
||||
status = "✅ Enabled" if webhook.enabled else "❌ Disabled"
|
||||
print(f"{status} {webhook.webhook_id}")
|
||||
print(f" URL: {webhook.url}")
|
||||
print(f" Events: {', '.join(webhook.event_types)}")
|
||||
if webhook.inbox_ids:
|
||||
print(f" Inboxes: {', '.join(webhook.inbox_ids)}")
|
||||
print(f" Created: {webhook.created_at}")
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error listing webhooks: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.delete:
|
||||
try:
|
||||
client.webhooks.delete(args.delete)
|
||||
print(f"✅ Webhook {args.delete} deleted successfully")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to delete webhook: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
print("Error: Must specify --create, --list, --delete, or --test-server")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
def start_test_server():
|
||||
"""Start a simple Flask webhook receiver for testing"""
|
||||
try:
|
||||
from flask import Flask, request, Response
|
||||
except ImportError:
|
||||
print("Error: flask package not found. Install with: pip install flask")
|
||||
sys.exit(1)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
return """
|
||||
<h1>AgentMail Webhook Test Server</h1>
|
||||
<p>✅ Server is running</p>
|
||||
<p>Webhook endpoint: <code>POST /webhook</code></p>
|
||||
<p>Check console output for incoming webhooks.</p>
|
||||
"""
|
||||
|
||||
@app.route('/webhook', methods=['POST'])
|
||||
def webhook():
|
||||
payload = request.json
|
||||
|
||||
print("\n🪝 Webhook received:")
|
||||
print(f" Event: {payload.get('event_type')}")
|
||||
print(f" ID: {payload.get('event_id')}")
|
||||
|
||||
if payload.get('event_type') == 'message.received':
|
||||
message = payload.get('message', {})
|
||||
print(f" From: {message.get('from', [{}])[0].get('email')}")
|
||||
print(f" Subject: {message.get('subject')}")
|
||||
print(f" Preview: {message.get('preview', '')[:50]}...")
|
||||
|
||||
print(f" Full payload: {json.dumps(payload, indent=2)}")
|
||||
print()
|
||||
|
||||
return Response(status=200)
|
||||
|
||||
print("🚀 Starting webhook test server on http://localhost:3000")
|
||||
print("📡 Webhook endpoint: http://localhost:3000/webhook")
|
||||
print("\n💡 For external access, use ngrok:")
|
||||
print(" ngrok http 3000")
|
||||
print("\n🛑 Press Ctrl+C to stop\n")
|
||||
|
||||
try:
|
||||
app.run(host='0.0.0.0', port=3000, debug=False)
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Webhook server stopped")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user