名称: 编程发送邮件 描述: “通过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)
- 症状:使用Gmail账户密码SMTP身份验证失败
- 解决方案:在https://myaccount.google.com/apppasswords生成应用密码(需要启用2FA)
错误:“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秒
另请参阅
- …/using-telegram-bot/SKILL.md — 通过Telegram的替代通知方法
- …/nostr-logging-system/SKILL.md — 去中心化日志记录替代方案
- …/user-aks-for-report/SKILL.md — 可电邮发送的报告生成