Mailgun用户手册

Mailgun用户手册

个人信息界面请参考Mailgun面板 仅介绍http接口的使用,不涉及smtp协议。 更多信息请参考官方文档

2. 发送邮件

2.1 发送基本文本


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient-1@example.com \
    -F to=recipient-2@example.com \
    -F subject='Hello there!' \
    -F text='Testing some Mailgun awesomeness!'

2.2 以文本和 HTML 版本发送


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F text='This will be the text-only version' \
    --form-string html='<html><body><p>This is the HTML version</p></body></html>'

2.3 发送附件


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F text='Testing some Mailgun awesomeness!' \
    -F attachment=@tps-report.txt \
    -F attachment=@cover-letter.txt

2.4 发送内联文件


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F inline=@logo.jpg \
    --form-string html='<html><body><p>Hello from <img src="cid:email.webp"/></p></body></html>'

2.5 自定义功能

2.5.1 仅对特定消息启用跟踪

虽然可以为仪表板中的所有消息启用跟踪,但你也可以有选择地针对每条消息启用跟踪。 要启用所有跟踪类型,请使用“o:tracking=“yes””参数。 否则,你可以仅启用对打开(‘o:tracking-opens’)或点击(‘o:tracking-clicks’)的特定跟踪:


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F text='Testing some Mailgun awesomeness!' \
    -F o:tracking-opens="yes"

2.5.2 特定时间发送

“o:deliverytime”选项允许你指定何时发送电子邮件。 它使用 RFC822 日期格式,并且不能超过未来 3 天(除非计费计划支持 7 天或更多天的存储能力):


curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F text='Testing some Mailgun awesomeness!' \
    -F o:deliverytime='Fri, 14 Oct 2011 23:10:10 -0000'

2.5.3 标记电子邮件

curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <postmaster@YOUR_DOMAIN_NAME>' \
    -F to=recipient@example.com \
    -F subject="Hello there!" \
    -F text='Testing some Mailgun awesomeness!' \
    -F o:tag='September newsletter' \
    -F o:tag='newsletters'

2.5.4 重新发送之前发送的电子邮件

curl -s --user 'api:YOUR_API_KEY' {{STORAGE.URL}} \
    -F to='bob@example.com, john@example.com'

2.5.5 批量发送

Mailgun 支持通过单个 API 调用或 SMTP 会话发送给一组收件人的功能。这是通过以下方式实现的:

  1. 通过指定多个收件人电子邮件地址作为 to 参数并使用收件人变量来使用批量发送。
  2. 将邮件列表与模板变量结合使用

更多信息请参考批量发送

2.5.6 邮件列表

邮件列表是使用电子邮件别名向多个收件人发送邮件的好方法。当你使用邮件列表时,Mailgun 将使用电子邮件别名向每个订阅成员发送邮件副本。你可以使用 API 或控制面板创建和维护订阅者列表。此外,你可以使用模板变量为邮件列表的每个成员创建唯一的消息。

更多信息请参考邮件列表

3. 追踪消息

3.1 启用跟踪

事件跟踪会自动启用,但取消订阅、打开和点击除外。

你可以通过控制面板的“域”选项卡为你的域启用取消订阅跟踪。你还可以使用取消订阅变量来管理每条消息的取消订阅。

你可以在两个级别启用打开和点击跟踪 - 每个发送域和每个消息。

  • 你可以在特定域设置页面的 “跟踪设置”部分 下针对每个域启用打开和点击跟踪 。
  • 发送消息时,还可以通过设置 o:tracking、o:tracking-clicks 和 o:tracking-opens 参数来切换跟踪。这将覆盖域级别设置。

你需要将 CNAME 记录指向 mailgun.org,以便 Mailgun 重写链接并跟踪打开情况。此外,邮件中需要有一个 HTML 部分,以便 Mailgun 跟踪打开情况。

3.2 事件跟踪

Mailgun 会自动跟踪以下事件:

  1. accepted:Mailgun 接受了发送/转发电子邮件的请求,并且邮件已放入队列中。
  2. rejected:Mailgun 拒绝了发送/转发电子邮件的请求。
  3. delivered:Mailgun 发送了电子邮件,并被收件人电子邮件服务器接受。
  4. failed:Mailgun 无法将电子邮件传送到收件人电子邮件服务器。
  5. opened:电子邮件收件人打开电子邮件并启用图像查看。必须在 Mailgun 控制面板中启用开放式跟踪,并且 CNAME 记录必须指向 mailgun.org。
  6. clicked:电子邮件收件人单击了电子邮件中的链接。必须在 Mailgun 控制面板中启用点击跟踪,并且 CNAME 记录必须指向 mailgun.org。
  7. unsubscribed:电子邮件收件人单击取消订阅链接。必须在 Mailgun 控制面板中启用取消订阅跟踪。
  8. complained:电子邮件收件人单击其电子邮件客户端中的垃圾邮件投诉按钮。反馈循环使 Mailgun 能够接收通知。
  9. stored:Mail 已存储传入消息。
  10. list_member_uploaded:此事件在成功将成员添加到邮件列表后发生。
  11. list_member_upload_error:如果将成员添加到邮件列表时发生错误,甚至会发生这种情况。
  12. list_uploaded:此事件在成功将大量成员上传到邮件列表后发生。

你可以通过几个接口访问事件:

  1. Webhooks(我们将数据发布到你的 URL)
  2. 事件 API(你通过 API 获取数据)
  3. 控制面板 (GUI) 的 日志选项 卡

3.3 网络钩子

当你的邮件发生事件时,Mailgun 可以向你的 URL 发出 HTTP/HTTPS POST。如果你希望 Mailgun 甚至可以发布通知,你需要在控制面板的Webhooks选项卡中提供回调 URL 。Webhook 位于域级别,它允许你使用域下拉选择器为每个域提供唯一的 URL。

  • 注意:如果要包含 HTTPS 端点,则必须使用受信任的 CA(证书颁发机构)签名的 SSL 证书(而不是自签名证书)对其进行配置。

你可以阅读有关下面相应部分中发布的数据的更多信息(跟踪打开、跟踪点击、跟踪退订、跟踪垃圾邮件投诉、跟踪失败、跟踪递送)。我们建议使用http://bin.mailgun.net/ 创建临时 URL 来测试和调试你的 Webhooks。

对于 Webhook POST,Mailgun 会侦听来自服务器的以下代码并做出相应反应:

  1. 如果 Mailgun 收到 200(成功)代码,它将确定 Webhook POST 成功并且不会重试。
  2. 如果 Mailgun 收到 406(不可接受)代码,Mailgun 将确定 POST 被拒绝并且不会重试。
  3. 对于任何其他代码,Mailgun 将根据以下 Webhooks 的时间表重试 POSTing(送达通知除外)。

如果你的应用程序无法处理 Webhook 请求,但你没有返回 406 错误代码,Mailgun 将在 8 小时内按以下时间间隔重试(送达通知除外),然后停止尝试:5 分钟、10 分钟、15 分钟分钟、1小时、2小时、4小时。

3.4 检测机器人

Mailgun 使用跟踪像素和 URL 重定向来跟踪用户何时打开邮件并单击电子邮件中的链接。但是,有各种第三方自动化系统会自动打开并发送消息并跟踪链接以进行病毒扫描和用户活动混淆,例如Apple Mail Privacy Protection。

由于自动化系统可能会影响打开和点击跟踪的准确性,因此 Mailgun 将尝试检测其中一个系统何时检索跟踪像素或单击链接。当检测到机器人打开或单击电子邮件中的链接时,Mailgun 将通过打开/单击事件中的 client-info.bot 字段进行指示。


{
    "client-info": {
      "client-name": "unknown",
      "client-type": "unknown",
      "user-agent": "Mozilla/5.0",
      "device-type": "unknown",
      "client-os": "unknown",
      "bot": "apple"
    },
    "tags": [],
    "timestamp": 1652883435.279025,
    "recipient": "bot@apple.com",
    "geolocation": {
      "region": "Unknown",
      "country": "US",
      "city": "Unknown"
    },
    "event": "opened",
}

bot 字段可以具有以下可能值之一:

  1. apple:表示 Apple MPP 机器人
  2. gmail:表示 Gmail 机器人
  3. generic:表示未知机器人(很可能是防火墙或防病毒扫描)
  4. (empty):如果机器人字段为空,则表示未检测到机器人。

4. 接收、转发和存储消息

Mailgun 将允许你通过路由接收电子邮件,该路由将执行以下操作:

  • 将电子邮件转发到不同的电子邮件地址
  • 将电子邮件中的数据发布到 URL
  • 暂时存储电子邮件以供后续通过 GET 请求检索

更多信息请参考接收、转发和存储消息

测试脚本

import json
import os
import random
import re
import requests
import threading
from jinja2 import Environment
from jinja2 import FileSystemLoader
from jinja2 import select_autoescape


def send_email(
        to_addresses,
        subject,
        text,
        tag,
        html_template=None,
        attachments=None,
        tracking_opens="no",
        tracking_clicks="no",
        from_address=os.getenv("MAILGUN_FROM_ADDRESS", None),
):
    api_key = os.getenv("MAILGUN_API_KEY", None)
    domain = os.getenv("MAILGUN_DOMAIN", None)
    if not api_key or not domain or not from_address:
        return None
    url = f"https://api.mailgun.net/v3/{domain}/messages"
    auth = ("api", api_key)

    data = {
        "from": from_address,
        "to": to_addresses,
        "subject": subject,
        "text": text,
        "o:tracking-opens": tracking_opens,
        "o:tracking-clicks": tracking_clicks,
        "o:tag": tag,
    }

    # 如果有HTML内容,添加到data中
    if html_template:
        data["html"] = html_template

    # 处理附件
    files = []
    if attachments:
        # 使用上下文管理器打开文件
        files = [("attachment", (attachment, open(attachment, "rb")))
                 for attachment in attachments]

    try:
        # 发送请求并检查错误
        response = requests.post(url, auth=auth, data=data, files=files)
        response.raise_for_status()  # 这将抛出异常,如果响应状态码表明一个错误
    except requests.exceptions.RequestException as e:
        # 对于所有请求相关的异常
        print(f"Error sending email: {e}")
        return None
    finally:
        # 无论请求成功还是抛出异常,确保所有打开的文件都被关闭
        for _, file_tuple in files:
            if isinstance(file_tuple, tuple):
                file_tuple[1].close()
    return response


def get_email_id(response):
    if response is None:
        return None
    response_dict = json.loads(response.text)
    # 如果存在id,返回id
    if "id" in response_dict:
        email_id = response_dict["id"]
        email_id_cleaned = re.sub(r"[<>]", "", email_id)
    else:
        email_id_cleaned = None
    return email_id_cleaned

# 发送注册验证码
def send_email_register_code(to_addresses, code, created_at):
    subject = "注册验证码"
    text = (f"你的注册验证码为:\n"
            f"{code}\n"
            f"有效期为5分钟\n"
            f"请注意,如果这不是你本人的操作,请忽略并关闭此邮件。\n")
    tag = "register"  # 标签
    html_template = render_template_direct("email/register.html", code=code)
    # 发送邮件
    response = send_email(to_addresses, subject, text, tag, html_template)
    email_id = get_email_id(response)

    return email_id

# jinja2模板渲染
def render_template_direct(template_name, **context):
    # 设置模板环境
    env = Environment(
        loader=FileSystemLoader("templates"),
        autoescape=select_autoescape(["html", "xml"]),
    )
    template = env.get_template(template_name)
    return template.render(**context)


if __name__ == "__main__":
    code = "".join([str(random.randint(0, 9)) for _ in range(6)])
    send_email_register_code("xx@xx", code)