编程发送邮件Skill send-email-programmatically

此技能用于通过编程方式自动化发送电子邮件,支持SMTP协议、免费API(如Mailgun和SendGrid)以及隐私服务。适用于发送通知、警报、自动化报告、错误日志和用户通信工作流。关键词:电子邮件发送、SMTP、API、自动化、编程邮件、通知系统、错误日志、邮件服务。

DevOps 0 次安装 0 次浏览 更新于 3/22/2026

名称: 编程发送邮件 描述: “通过SMTP、免费API或隐私服务发送电子邮件。适用于:(1) 发送通知和警报,(2) 自动化报告和摘要,(3) 用户通信工作流,或(4) 通过电子邮件进行错误日志记录。”

编程发送邮件

通过SMTP、免费API(Mailgun、SendGrid免费层)或隐私服务发送电子邮件。适用于通知、警报、自动化报告和用户通信。

何时使用

  • 用例1:当用户要求发送电子邮件通知或警报时
  • 用例2:当需要交付自动化报告或摘要时
  • 用例3:用于错误日志记录和监控警报
  • 用例4:当构建用户通信工作流时(确认、更新)

所需工具/API

  • curl — 用于基于API的电子邮件发送(大多数系统预安装)
  • sendmail / msmtp — 用于SMTP电子邮件发送
  • Mailgun API — 免费层:5,000封电子邮件/月(需要API密钥)
  • SendGrid API — 免费层:100封电子邮件/天(需要API密钥)
  • mail.tm — 临时电子邮件API(无需API密钥)

安装选项:

# Ubuntu/Debian
sudo apt-get install -y curl msmtp msmtp-mta

# macOS(postfix预安装,或使用msmtp)
brew install msmtp

# Node.js
npm install nodemailer

技能

send_email_via_smtp_curl

使用curl通过SMTP发送电子邮件(适用于Gmail、Outlook、自定义SMTP服务器)。

# Gmail SMTP示例(需要应用密码)
SMTP_SERVER="smtp.gmail.com:587"
FROM_EMAIL="your-email@gmail.com"
TO_EMAIL="recipient@example.com"
SUBJECT="测试邮件"
BODY="这是通过SMTP发送的测试邮件。"
APP_PASSWORD="your-app-password"

# 发送邮件
curl -v --url "smtp://${SMTP_SERVER}" \
  --mail-from "${FROM_EMAIL}" \
  --mail-rcpt "${TO_EMAIL}" \
  --user "${FROM_EMAIL}:${APP_PASSWORD}" \
  --upload-file - <<EOF
From: ${FROM_EMAIL}
To: ${TO_EMAIL}
Subject: ${SUBJECT}

${BODY}
EOF

# Outlook/Office365 SMTP
curl --url "smtp://smtp.office365.com:587" \
  --mail-from "your-email@outlook.com" \
  --mail-rcpt "recipient@example.com" \
  --user "your-email@outlook.com:your-password" \
  --ssl-reqd \
  --upload-file - <<EOF
From: your-email@outlook.com
To: recipient@example.com
Subject: 来自Outlook的问候

这是一封自动化邮件。
EOF

send_email_via_mailgun_api

使用Mailgun免费层发送电子邮件(5,000封/月,沙盒无需信用卡)。

# 设置您的Mailgun凭据
MAILGUN_API_KEY="your-mailgun-api-key"
MAILGUN_DOMAIN="sandbox123.mailgun.org"  # 或您的已验证域名

# 发送邮件
curl -s --user "api:${MAILGUN_API_KEY}" \
  "https://api.mailgun.net/v3/${MAILGUN_DOMAIN}/messages" \
  -F from="发件人名称 <mailgun@${MAILGUN_DOMAIN}>" \
  -F to="recipient@example.com" \
  -F subject="来自Mailgun的问候" \
  -F text="这是纯文本正文" \
  -F html="<h1>HTML邮件</h1><p>这是HTML正文</p>"

# 发送附件
curl -s --user "api:${MAILGUN_API_KEY}" \
  "https://api.mailgun.net/v3/${MAILGUN_DOMAIN}/messages" \
  -F from="notifications@${MAILGUN_DOMAIN}" \
  -F to="user@example.com" \
  -F subject="报告附件" \
  -F text="请查收附件报告。" \
  -F attachment=@./report.pdf

Node.js:

async function sendEmailMailgun(options) {
  const { apiKey, domain, from, to, subject, text, html } = options;
  
  const formData = new URLSearchParams({
    from,
    to,
    subject,
    text: text || '',
    html: html || ''
  });
  
  const auth = 'Basic ' + Buffer.from(`api:${apiKey}`).toString('base64');
  
  const res = await fetch(`https://api.mailgun.net/v3/${domain}/messages`, {
    method: 'POST',
    headers: {
      'Authorization': auth,
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: formData
  });
  
  if (!res.ok) {
    const error = await res.text();
    throw new Error(`Mailgun API错误: ${error}`);
  }
  
  return await res.json();
}

// 使用
// sendEmailMailgun({
//   apiKey: 'your-mailgun-api-key',
//   domain: 'sandbox123.mailgun.org',
//   from: '发件人 <mailgun@sandbox123.mailgun.org>',
//   to: 'recipient@example.com',
//   subject: '测试邮件',
//   text: '纯文本正文',
//   html: '<h1>HTML正文</h1>'
// }).then(result => console.log('邮件已发送:', result));

send_email_via_sendgrid_api

使用SendGrid免费层发送电子邮件(100封/天)。

# 设置您的SendGrid API密钥
SENDGRID_API_KEY="your-sendgrid-api-key"

# 发送邮件
curl -s --request POST \
  --url "https://api.sendgrid.com/v3/mail/send" \
  --header "Authorization: Bearer ${SENDGRID_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{
    "personalizations": [{
      "to": [{"email": "recipient@example.com"}],
      "subject": "来自SendGrid的问候"
    }],
    "from": {"email": "sender@example.com", "name": "发件人名称"},
    "content": [{
      "type": "text/plain",
      "value": "这是邮件正文。"
    }]
  }'

# 发送HTML邮件带附件
curl -s --request POST \
  --url "https://api.sendgrid.com/v3/mail/send" \
  --header "Authorization: Bearer ${SENDGRID_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{
    "personalizations": [{
      "to": [{"email": "user@example.com"}]
    }],
    "from": {"email": "notifications@example.com"},
    "subject": "周报",
    "content": [{
      "type": "text/html",
      "value": "<h1>周报</h1><p>附件是您的报告。</p>"
    }],
    "attachments": [{
      "content": "'"$(base64 -w 0 report.pdf)"'",
      "filename": "report.pdf",
      "type": "application/pdf"
    }]
  }'

Node.js:

async function sendEmailSendGrid(options) {
  const { apiKey, from, to, subject, text, html, attachments = [] } = options;
  
  const payload = {
    personalizations: [{
      to: [{ email: to }],
      subject
    }],
    from: { email: from },
    content: [{
      type: html ? 'text/html' : 'text/plain',
      value: html || text
    }]
  };
  
  if (attachments.length > 0) {
    payload.attachments = attachments.map(att => ({
      content: att.content,  // base64字符串
      filename: att.filename,
      type: att.type || 'application/octet-stream'
    }));
  }
  
  const res = await fetch('https://api.sendgrid.com/v3/mail/send', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!res.ok) {
    const error = await res.text();
    throw new Error(`SendGrid API错误: ${error}`);
  }
  
  return { success: true, status: res.status };
}

// 使用
// sendEmailSendGrid({
//   apiKey: 'your-sendgrid-api-key',
//   from: 'sender@example.com',
//   to: 'recipient@example.com',
//   subject: '测试邮件',
//   html: '<h1>你好</h1><p>这是一个测试。</p>'
// }).then(result => console.log('邮件已发送:', result));

send_email_via_msmtp

使用msmtp发送电子邮件(轻量级SMTP客户端,适合自动化)。

# 配置msmtp(一次性设置)
cat > ~/.msmtprc <<EOF
defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        ~/.msmtp.log

# Gmail账户
account        gmail
host           smtp.gmail.com
port           587
from           your-email@gmail.com
user           your-email@gmail.com
password       your-app-password

# 设置默认账户
account default : gmail
EOF

chmod 600 ~/.msmtprc

# 发送邮件
echo -e "Subject: 测试邮件
From: your-email@gmail.com
To: recipient@example.com

这是邮件正文。" | msmtp recipient@example.com

# 发送文件内容
cat report.txt | msmtp -t <<EOF
To: recipient@example.com
From: sender@gmail.com
Subject: 日报

$(cat report.txt)
EOF

# 发送HTML邮件
msmtp recipient@example.com <<EOF
To: recipient@example.com
From: sender@gmail.com
Subject: HTML邮件
Content-Type: text/html

<html>
<body>
  <h1>你好</h1>
  <p>这是一封HTML邮件。</p>
</body>
</html>
EOF

send_email_nodejs_nodemailer

使用Node.js nodemailer库发送电子邮件(支持所有SMTP服务器)。

Node.js:

const nodemailer = require('nodemailer');

async function sendEmail(config) {
  const {
    smtpHost,
    smtpPort,
    smtpUser,
    smtpPassword,
    from,
    to,
    subject,
    text,
    html,
    attachments = []
  } = config;
  
  // 创建传输器
  const transporter = nodemailer.createTransport({
    host: smtpHost,
    port: smtpPort,
    secure: smtpPort === 465, // 465端口为true,其他为false
    auth: {
      user: smtpUser,
      pass: smtpPassword
    },
    connectionTimeout: 10000
  });
  
  // 发送邮件
  const info = await transporter.sendMail({
    from,
    to,
    subject,
    text,
    html,
    attachments: attachments.map(att => ({
      filename: att.filename,
      path: att.path || undefined,
      content: att.content || undefined
    }))
  });
  
  return {
    success: true,
    messageId: info.messageId,
    response: info.response
  };
}

// 使用 - Gmail
// sendEmail({
//   smtpHost: 'smtp.gmail.com',
//   smtpPort: 587,
//   smtpUser: 'your-email@gmail.com',
//   smtpPassword: 'your-app-password',
//   from: '\"发件人名称\" <your-email@gmail.com>',
//   to: 'recipient@example.com',
//   subject: '你好',
//   text: '纯文本正文',
//   html: '<b>HTML正文</b>',
//   attachments: [
//     { filename: 'report.pdf', path: './report.pdf' }
//   ]
// }).then(result => console.log('邮件已发送:', result));

// 使用 - Outlook
// sendEmail({
//   smtpHost: 'smtp.office365.com',
//   smtpPort: 587,
//   smtpUser: 'your-email@outlook.com',
//   smtpPassword: 'your-password',
//   from: 'your-email@outlook.com',
//   to: 'recipient@example.com',
//   subject: '测试',
//   text: '这是一封测试邮件'
// });

advanced_email_with_retry

生产就绪的电子邮件发送,带重试逻辑和错误处理。

#!/bin/bash
send_email_with_retry() {
  local TO="$1"
  local SUBJECT="$2"
  local BODY="$3"
  local MAX_RETRIES=3
  local RETRY_DELAY=5
  
  for i in $(seq 1 $MAX_RETRIES); do
    if curl -fsS --max-time 30 \
      --url "smtp://smtp.gmail.com:587" \
      --mail-from "sender@gmail.com" \
      --mail-rcpt "$TO" \
      --user "sender@gmail.com:app-password" \
      --upload-file - <<EOF
From: sender@gmail.com
To: $TO
Subject: $SUBJECT

$BODY
EOF
    then
      echo "邮件成功发送至 $TO"
      return 0
    else
      echo "尝试 $i 失败,${RETRY_DELAY}秒后重试..." >&2
      sleep $RETRY_DELAY
    fi
  done
  
  echo "$MAX_RETRIES 次尝试后邮件发送失败" >&2
  return 1
}

# 使用
send_email_with_retry "user@example.com" "警报" "系统CPU使用率过高"

Node.js:

async function sendEmailWithRetry(config, maxRetries = 3) {
  const { provider, ...emailConfig } = config;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      let result;
      
      if (provider === 'mailgun') {
        result = await sendEmailMailgun(emailConfig);
      } else if (provider === 'sendgrid') {
        result = await sendEmailSendGrid(emailConfig);
      } else {
        throw new Error(`未知提供商: ${provider}`);
      }
      
      return { success: true, attempt, result };
      
    } catch (err) {
      console.error(`尝试 ${attempt} 失败:`, err.message);
      
      if (attempt === maxRetries) {
        throw new Error(`${maxRetries} 次尝试后邮件发送失败: ${err.message}`);
      }
      
      // 指数退避
      const delayMs = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }
}

// 使用
// sendEmailWithRetry({
//   provider: 'mailgun',
//   apiKey: 'your-api-key',
//   domain: 'sandbox123.mailgun.org',
//   from: 'sender@sandbox123.mailgun.org',
//   to: 'recipient@example.com',
//   subject: '关键警报',
//   text: '系统需要关注'
// }, 3).then(result => console.log('邮件已发送:', result));

速率限制/最佳实践

  • 使用应用密码 — 对于Gmail/Outlook,创建应用特定密码而非账户密码
  • 免费层限制 — Mailgun: 5,000/月,SendGrid: 100/天,Gmail: 500/天
  • 重试逻辑 — 为失败发送实现指数退避
  • 错误处理 — 捕获和记录错误,不要在日志中暴露凭据
  • 验证电子邮件 — 发送前检查收件人电子邮件格式
  • SPF/DKIM — 配置DNS记录用于自定义域名以避免垃圾邮件
  • ⚠️ 绝不硬编码凭据 — 使用环境变量或安全保险库
  • ⚠️ 速率限制 — 批量发送间添加延迟(1-2秒)
  • ⚠️ 弹回处理 — 监控弹回并移除无效地址

代理提示

您具有通过SMTP和免费API发送电子邮件的能力。当用户要求发送电子邮件时:

1. 基于要求选择最佳方法:
   - **curl + SMTP** — 用于简单的Gmail/Outlook电子邮件(需要应用密码)
   - **Mailgun API** — 用于较高量(5,000/月免费,需要API密钥)
   - **SendGrid API** — 用于中等量(100/天免费,需要API密钥)
   - **msmtp** — 用于自动化脚本和cron作业
   - **nodemailer** — 用于具有完整SMTP支持的Node.js应用

2. 对于Gmail/Outlook SMTP:
   - Gmail: smtp.gmail.com:587(需要来自Google账户设置的应用密码)
   - Outlook: smtp.office365.com:587
   - 始终使用应用密码,绝不使用账户密码

3. 对于Mailgun:
   - 免费沙盒域名:5,000封电子邮件/月发给授权收件人
   - 已验证域名:无限收件人(在免费层限制内)
   - 沙盒无需信用卡

4. 对于SendGrid:
   - 免费层:100封电子邮件/天
   - 需要账户注册和API密钥

5. 始终:
   - 验证收件人电子邮件格式
   - 使用环境变量存储凭据
   - 实现重试逻辑(3次尝试带指数退避)
   - 优雅处理错误并提供清晰消息
   - 绝不记录凭据或敏感数据

6. 安全:
   - 将SMTP密码存储在~/.msmtprc(chmod 600)或环境变量中
   - 对所有SMTP连接使用TLS/SSL
   - 验证和清理电子邮件内容以防止注入

故障排除

错误:“身份验证失败”(Gmail)

错误:“530 5.7.0 必须先发出STARTTLS命令”

  • 症状:服务器需要TLS加密
  • 解决方案:添加--ssl-reqd到curl命令或使用端口587/465

错误:“554 5.7.1 中继访问被拒绝”

  • 症状:SMTP服务器拒绝电子邮件中继
  • 解决方案:使用正确的凭据进行身份验证,确保FROM电子邮件匹配已验证账户

Mailgun:“免费账户仅用于测试目的”

  • 症状:Mailgun沙盒限制收件人
  • 解决方案:在Mailgun仪表板中添加授权收件人,或验证自定义域名

SendGrid:“403 禁止访问”

  • 症状:API密钥缺少权限
  • 解决方案:在SendGrid仪表板中创建具有"邮件发送"权限的新API密钥

电子邮件进入垃圾邮件:

  • 症状:收件人未收到电子邮件或在垃圾文件夹中
  • 解决方案:为自定义域名配置SPF、DKIM、DMARC DNS记录;使用已验证的发件人电子邮件

超时错误:

  • 症状:SMTP连接挂起或超时
  • 解决方案:检查防火墙允许出站连接端口587/465;增加超时至30秒

另请参阅