Guides6 min read

Email Automation with AI Chatbots

Set up AI-powered email automation with OpenClaw or OpenClaw. Automate outreach, responses, and email management through your VPS.

Published: 27/01/2025

Overview

Combine your AI chatbot with email automation for powerful workflows. From drafting responses to managing outreach campaigns, your VPS-hosted bot can handle email tasks triggered by chat commands.

Use Cases

  • Email Drafting: "Draft a follow-up email to John about the project"
  • Response Automation: Auto-reply to common inquiries
  • Outreach Management: Prepare personalized emails at scale
  • Email Summaries: "Summarize my unread emails"
  • Follow-up Reminders: Track and prompt follow-ups

Architecture

Chat Command (Discord/WhatsApp)
    ↓
[VPS - OpenClaw/OpenClaw]
    ↓
[AI generates email content]
    ↓
[Email sent via SMTP]

Basic Setup

1. Install Dependencies

npm install nodemailer

2. Environment Configuration

# SMTP Settings
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your@gmail.com
SMTP_PASSWORD=app-specific-password
SMTP_FROM=Your Name <your@gmail.com>

# Email Features
EMAIL_ENABLED=true
EMAIL_DRAFT_MODE=true  # Review before sending

3. Basic Email Function

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: process.env.SMTP_PORT,
  secure: false,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASSWORD
  }
});

async function sendEmail({ to, subject, body }) {
  const info = await transporter.sendMail({
    from: process.env.SMTP_FROM,
    to,
    subject,
    text: body,
    html: body.replace(/\n/g, '<br>')
  });

  return info.messageId;
}

Chat-to-Email Workflow

Draft Mode (Recommended)

Review emails before sending:

async function handleEmailCommand(message) {
  const prompt = message.content.replace('/email', '').trim();

  // Generate email with AI
  const draft = await generateEmailDraft(prompt);

  // Store for review
  const draftId = await saveDraft(draft);

  return `📧 **Email Draft #${draftId}**

**To:** ${draft.to}
**Subject:** ${draft.subject}

${draft.body}

---
Reply \`/send ${draftId}\` to send, or \`/edit ${draftId}\` to modify.`;
}

AI Email Generation

async function generateEmailDraft(instruction) {
  const response = await claude.messages.create({
    model: 'claude-3-5-sonnet-20241022',
    messages: [{
      role: 'user',
      content: `Generate a professional email based on this instruction:

${instruction}

Return in this JSON format:
{
  "to": "recipient@example.com",
  "subject": "Email subject",
  "body": "Email body text"
}

If no recipient specified, use [RECIPIENT] as placeholder.`
    }]
  });

  return JSON.parse(response.content[0].text);
}

Example Interaction

User: /email follow up with sarah@company.com about the proposal we discussed last week

Bot:

📧 Email Draft #42

To: sarah@company.com
Subject: Following Up on Our Proposal Discussion

Hi Sarah,

I hope this email finds you well. I wanted to follow up on the proposal
we discussed last week.

Have you had a chance to review the details? I'd be happy to answer any
questions or clarify any points that might be helpful for your decision.

Please let me know if you'd like to schedule a call to discuss further.

Best regards,
[Your Name]

---
Reply `/send 42` to send, or `/edit 42` to modify.

Advanced Features

Email Templates

Store common templates:

const TEMPLATES = {
  follow_up: {
    subject: 'Following Up',
    body: `Hi {name},

I wanted to follow up on our previous conversation about {topic}.

{custom_content}

Best regards,
{sender_name}`
  },
  meeting_request: {
    subject: 'Meeting Request: {topic}',
    body: `Hi {name},

I'd like to schedule a meeting to discuss {topic}.

Would any of these times work for you?
{available_times}

Looking forward to hearing from you.

Best regards,
{sender_name}`
  }
};

async function useTemplate(templateName, variables) {
  const template = TEMPLATES[templateName];
  let { subject, body } = template;

  for (const [key, value] of Object.entries(variables)) {
    subject = subject.replace(`{${key}}`, value);
    body = body.replace(new RegExp(`{${key}}`, 'g'), value);
  }

  return { subject, body };
}

Contact Management

Simple contact storage:

const contacts = new Map();

async function addContact(email, name, notes) {
  contacts.set(email, { name, notes, lastContact: null });
}

async function getContactContext(email) {
  const contact = contacts.get(email);
  if (contact) {
    return `Recipient: ${contact.name}
Previous notes: ${contact.notes}
Last contact: ${contact.lastContact}`;
  }
  return '';
}

Outreach Campaigns

Prepare bulk personalized emails:

async function prepareOutreach(recipients, template, customData) {
  const drafts = [];

  for (const recipient of recipients) {
    const personalized = await claude.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      messages: [{
        role: 'user',
        content: `Personalize this email for ${recipient.name} at ${recipient.company}:

Template: ${template}

Their context: ${recipient.context}

Keep the core message but add relevant personalization.`
      }]
    });

    drafts.push({
      to: recipient.email,
      subject: customData.subject,
      body: personalized.content[0].text
    });
  }

  return drafts;
}

Command: /outreach campaign_name

Bot:

📧 Prepared 15 emails for campaign "Q1 Outreach"

Preview first 3:

1. john@acme.com - Personalized for software industry
2. sarah@techco.com - Mentioned their recent funding
3. mike@startup.io - Referenced mutual connection

Reply `/review-outreach campaign_name` to review all
Reply `/send-outreach campaign_name` to send all

Email Reading (IMAP)

Setup IMAP

npm install imap mailparser
const Imap = require('imap');
const { simpleParser } = require('mailparser');

const imap = new Imap({
  user: process.env.IMAP_USER,
  password: process.env.IMAP_PASSWORD,
  host: process.env.IMAP_HOST,
  port: 993,
  tls: true
});

async function getUnreadEmails() {
  return new Promise((resolve, reject) => {
    imap.once('ready', () => {
      imap.openBox('INBOX', false, (err, box) => {
        imap.search(['UNSEEN'], (err, results) => {
          // Fetch and parse emails
          const fetch = imap.fetch(results, { bodies: '' });
          const emails = [];

          fetch.on('message', (msg) => {
            msg.on('body', async (stream) => {
              const parsed = await simpleParser(stream);
              emails.push({
                from: parsed.from.text,
                subject: parsed.subject,
                date: parsed.date,
                text: parsed.text
              });
            });
          });

          fetch.once('end', () => {
            imap.end();
            resolve(emails);
          });
        });
      });
    });

    imap.connect();
  });
}

Email Summary Command

async function handleSummaryCommand() {
  const emails = await getUnreadEmails();

  if (emails.length === 0) {
    return "📭 No unread emails!";
  }

  const summary = await claude.messages.create({
    model: 'claude-3-5-sonnet-20241022',
    messages: [{
      role: 'user',
      content: `Summarize these ${emails.length} emails briefly:

${emails.map(e => `From: ${e.from}
Subject: ${e.subject}
Preview: ${e.text.substring(0, 200)}...
---`).join('\n')}`
    }]
  });

  return `📧 **Email Summary (${emails.length} unread)**\n\n${summary.content[0].text}`;
}

Security Considerations

Never Store Plain Passwords

# Use app-specific passwords
SMTP_PASSWORD=xxxx-xxxx-xxxx-xxxx  # Gmail app password

# Or OAuth2 (more secure)
GMAIL_CLIENT_ID=...
GMAIL_CLIENT_SECRET=...
GMAIL_REFRESH_TOKEN=...

Limit Send Permissions

const ALLOWED_SENDERS = ['your_discord_id'];

async function handleEmailCommand(message) {
  if (!ALLOWED_SENDERS.includes(message.author.id)) {
    return "You don't have permission to send emails.";
  }
  // Continue...
}

Rate Limiting

const sendLimits = new Map();

function checkSendLimit(userId) {
  const today = new Date().toDateString();
  const key = `${userId}-${today}`;
  const sent = sendLimits.get(key) || 0;

  if (sent >= 50) {
    return false;
  }

  sendLimits.set(key, sent + 1);
  return true;
}

Confirmation for External Recipients

const INTERNAL_DOMAINS = ['yourcompany.com', 'yourdomain.co.uk'];

async function validateRecipient(email) {
  const domain = email.split('@')[1];

  if (!INTERNAL_DOMAINS.includes(domain)) {
    return {
      needsConfirmation: true,
      message: `⚠️ External recipient detected (${domain}). Confirm with /confirm-send`
    };
  }

  return { needsConfirmation: false };
}

Best Practices

Do's

  • ✅ Always use draft mode for new setups
  • ✅ Log all sent emails
  • ✅ Implement rate limiting
  • ✅ Review AI-generated content before sending
  • ✅ Use app-specific passwords

Don'ts

  • ❌ Auto-send without review
  • ❌ Store credentials in code
  • ❌ Allow unlimited sends
  • ❌ Skip recipient validation
  • ❌ Use for spam or cold outreach abuse

Compliance Notes

GDPR/UK DPA

  • Only email contacts who've consented
  • Include unsubscribe option for marketing
  • Keep records of consent
  • Don't scrape emails

CAN-SPAM/PECR

  • Include physical address in marketing emails
  • Honor unsubscribe requests promptly
  • Clear sender identification

Related Guides

Need Help?

Email automation requires careful setup. Our premium service includes email integration with proper security configuration.

Need a VPS for Your Bot?

We recommend Hostinger KVM 2 VPS - reliable, fast, and perfect for AI chatbots. Get started with our recommended setup.

Get Hostinger VPS

Need Help With Setup?

Got your VPS? Let us handle the technical work. Professional setup and maintenance for OpenClaw (formerly Clawd.bot).