rommea/mail-tracker

记录并跟踪所有来自Laravel的发送邮件

7.14 2024-04-10 09:05 UTC

README

Latest Version on Packagist Software License Total Downloads Travis

MailTracker将连接到Laravel的所有发送邮件,并将其注入跟踪代码。它还会将渲染的邮件存储在数据库中。还提供了一个界面来查看已发送的邮件。

注意:如果您正在使用Laravel 9.x,您必须使用9.6.0或更高版本。

从4.x升级到5.x

在4.x及之前的版本中,sent_emails表中有一个recipient列,用于存储RFC标准的电子邮件格式<Name> email@example.com。5.x已更新为分别存储名称和电子邮件。已从sent_emails表的迁移中删除了recipientsender列,但没有添加迁移来删除这些列。已添加访问器以保留模型的recipientsender属性,因此除非您专门查询senderrecipient列,否则不需要更新任何使用这些列的现有代码。

为了在升级后保留现有数据,请运行artisan命令mail-tracker:migrate-recipients。这将把现有的recipientsender数据转换为新的格式。在此之后,您可以删除recipientsender列。

在sent_emails表中还有一对新的属性:opened_atclicked_at。分别存储用户首次打开和点击的时间。这已被添加到默认跟踪索引页面。您可以将它添加到您的页面,或根据需要使用这些值。

从3.x升级到4.x

从3.x升级到4.x没有破坏性变化,除了4.x仅适用于Laravel 7+。

从2.x升级到3.x

在更新到3.0版本时,有一个破坏性更改,特别是有关触发的事件。如果您正在监听PermanentBouncedMessageEvent以捕获所有不可投递的邮件,现在有两个单独的事件:PermanentBouncedMessageEvent将仅在永久退回时触发,一个新的事件ComplaintMessageEvent将在投诉时触发。还有一个新的EmailDeliveredEvent事件,在每次成功投递事件时触发。有关设置SES/SNS环境以接收有关这些事件的通告的信息,请参阅下面的文档。

从2.0或更早版本升级

首先,通过运行以下命令升级到2.2版本:

composer require jdavidbakr/mail-tracker ~2.2

如果您正在更新早期版本,您需要更新配置文件并运行新的迁移。为了获得最佳效果,请备份config/mail-tracker.php和resources/views/vendor/emailTrackingViews中的视图(如果存在),以恢复您自定义的任何值,然后删除该文件并运行

php artisan vendor:publish
php artisan migrate

请注意,由于2.1.13版本,sent_emails_url_clicked表的迁移发生了变化。变化是URL列现在是TEXT字段,以便允许更长的URL。如果您有一个旧的系统,您可能需要手动更改该列;没有迁移来执行此更新。

安装

通过Composer

composer require jdavidbakr/mail-tracker

发布配置文件和迁移

php artisan vendor:publish --provider="jdavidbakr\MailTracker\MailTrackerServiceProvider"

运行迁移

php artisan migrate

注意:如果您想使用不同的连接来存储您的模型,您应该在运行迁移之前更新mail-tracker.php配置条目connection

如果您想使用自己的迁移,您可以通过调用MailTracker::ignoreMigrations()跳过此库的迁移。例如

// In AppServiceProvider

public function boot()
{
    MailTracker::ignoreMigrations();
}

用法

安装后,所有发出的邮件都会记录到数据库中。以下配置选项在config/mail-tracker.php中可用。

  • name:设置您的应用程序名称。
  • inject-pixel:将其设置为true,将在所有发出的HTML邮件中注入跟踪像素。
  • track-links:将其设置为true,将重写所有锚点href链接以包含跟踪链接。链接将使用户返回到您的网站,然后记录点击后将其重定向到最终目的地。
  • expire-days:在您的数据库中保留邮件的天数。如果您发送大量邮件,您可能希望最终将其过期。将其设置为零以从不从数据库中清除旧邮件。
  • route:跟踪URL的路由信息。根据需要设置前缀和中间件。
  • admin-route:管理员的路线信息。设置前缀和中间件。
  • admin-template:管理面板和视图的参数。您可以将现有的管理面板与MailTracker管理面板集成。
  • date-format:您可以在管理面板中定义显示日期的格式。
  • content-max-size:您可以通过覆盖默认的content数据库字段的长度限制。如果您需要使其更长,不要忘记将其类型从text更新。

如果您不希望跟踪电子邮件,则可以在消息中添加X-No-Track标题。将任何随机字符串放入此标题中以阻止跟踪。在发送之前将从电子邮件中删除此标题。

\Mail::send('email.test', [], function ($message) {
    // ... other settings here
    $message->getHeaders()->addTextHeader('X-No-Track',Str::random(10));
});

将邮件内容存储在文件系统中

默认情况下,电子邮件内容存储在数据库中的content列中,以便在发送后可以查看电子邮件。如果发送了大量的电子邮件,这可能会消耗大量的内存并减慢数据库的整体速度。可以在配置中指定内容应保存到文件系统中的文件。

    'log-content-strategy' => 'filesystem',
    'tracker-filesystem' => null
    'tracker-filesystem-folder' => 'mail-tracker',

要使用文件系统,需要将log-content-strategydatabase更改为filesystem。您可以使用tracker-filesystem指定磁盘,并使用tracker-filesystem-folder指定它应存储文件的文件夹。

覆盖模型

在某些情况下,您可能想要覆盖内置模型。例如,您可以在AppServiceProvider中轻松地这样做:

MailTracker::useSentEmailModel(YourOwnSentEmailModel::class);
MailTracker::useSentEmailUrlClickedModel(YourOwnSentEmailUrlClickedModel::class);

您的模型应实现SentEmailModelSentEmailUrlClickedModel接口。此包提供了特质,以便轻松实现自己的模型,而无需重新实现或复制现有代码。

use Illuminate\Database\Eloquent\Model;
use jdavidbakr\MailTracker\Concerns\IsSentEmailModel;
use jdavidbakr\MailTracker\Contracts\SentEmailModel;

class OwnEmailSentModel extends Model implements SentEmailModel {
    use IsSentEmailModel;

    protected static $unguarded = true;

    protected $casts = [
        'meta' => 'collection',
        'opened_at' => 'datetime',
        'clicked_at' => 'datetime',
    ];
}

跳过特定电子邮件的跟踪

如果您有不想跟踪的特定电子邮件,则可以在电子邮件中添加X-No-Track标题。这将阻止跟踪电子邮件。在发送之前将从电子邮件中删除此标题。

从laravel 9开始,您可以将headers方法引入Mailable类。这将阻止跟踪像素/点击跟踪应用于Mailable。

public function headers()
{
    return [
        'X-No-Track' => Str::random(10),
    ];
}

跳过防病毒/垃圾邮件过滤器的打开/点击跟踪

某些邮件服务器在交付之前可能会扫描电子邮件,这可能会触发跟踪像素,甚至点击链接。您可以通过添加事件监听器来处理此事件。

class ValidUserListener {
    public function handle(ValidActionEvent $event)
    {
        if (in_array(request()->userAgent(), ['Mozilla/5.0', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246 Mozilla/5.0']) {
            $event->skip = true;
        }
    }
}

确保您已将监听器添加到您的EventServiceProvider中的ValidActionEvent,如果您不使用自动事件发现。

## Note on dev testing

Several people have reported the tracking pixel not working while they were testing. What is happening with the tracking pixel is that the email client is connecting to your website to log the view. In order for this to happen, images have to be visible in the client, and the client has to be able to connect to your server.

When you are in a dev environment (i.e. using the `.test` domain with Valet, or another domain known only to your computer) you must have an email client on your computer. Further complicating this is the fact that Gmail and some other web-based email clients don't connect to the images directly, but instead connect via proxy. That proxy won't have a connection to your `.test` domain and therefore will not properly track emails. I always recommend using [mailtrap.io](https://mailtrap.io) for any development environment when you are sending emails. Not only does this solve the issue (mailtrap.io does not use a proxy service to forward images in the emails) but it also protects you from accidentally sending real emails from your test environment.

## Events

When an email is sent, viewed, or a link is clicked, its tracking information is counted in the database using the jdavidbakr\MailTracker\Model\SentEmail model. This processing is done via dispatched jobs to the queue in order to prevent the database from being overwhelmed in an email blast situation. You may choose the queue that these events are dispatched via the `mail-tracker.tracker-queue` config setting, or leave it `null` to use the default queue. By using a non-default queue, you can prioritize application-critical tasks above these tracking tasks.

You may want to do additional processing on these events, so an event is fired in these cases:

-   jdavidbakr\MailTracker\Events\EmailSentEvent
    - Public attribute `sent_email` contains the `SentEmail` model
-   jdavidbakr\MailTracker\Events\ViewEmailEvent
    - Public attribute `sent_email` contains the `SentEmail` model
    - Public attribute `ip_address` contains the IP address that was used to trigger the event
-   jdavidbakr\MailTracker\Events\LinkClickedEvent
    - Public attribute `sent_email` contains the `SentEmail` model
    - Public attribute `ip_address` contains the IP address that was used to trigger the event
    - Public attribute `link_url` contains the clicked URL

If you are using the Amazon SNS notification system, these events are fired so you can do additional processing.

-   jdavidbakr\MailTracker\Events\EmailDeliveredEvent (when you received a "message delivered" event, you may want to mark the email as "good" or "delivered" in your database)
    - Public attribute `sent_email` contains the `SentEmail` model
    - Public attribute `email_address` contains the specific address that was used to trigger the event
-   jdavidbakr\MailTracker\Events\ComplaintMessageEvent (when you received a complaint, ex: marked as "spam", you may want to remove the email from your database)
    - Public attribute `sent_email` contains the `SentEmail` model
    - Public attribute `email_address` contains the specific address that was used to trigger the event
-   jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent (when you receive a permanent bounce, you may want to mark the email as bad or remove it from your database)
    jdavidbakr\MailTracker\Events\TransientBouncedMessageEvent (when you receive a transient bounce.  Check the event's public attributes for `bounce_sub_type` and `diagnostic_code` to determine if you want to do additional processing when this event is received.)
    - Public attribute `sent_email` contains the `SentEmail` model
    - Public attribute `email_address` contains the specific address that was used to trigger the event

To install an event listener, you will want to create a file like the following:

```php
<?php

namespace App\Listeners;

use jdavidbakr\MailTracker\Events\ViewEmailEvent;

class EmailViewed
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  ViewEmailEvent  $event
     * @return void
     */
    public function handle(ViewEmailEvent $event)
    {
        // Access the model using $event->sent_email
        // Access the IP address that triggered the event using $event->ip_address
    }
}
<?php

namespace App\Listeners;

use jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent;

class BouncedEmail
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  PermanentBouncedMessageEvent  $event
     * @return void
     */
    public function handle(PermanentBouncedMessageEvent $event)
    {
        // Access the email address using $event->email_address
    }
}

然后您必须在您的\App\Providers\EventServiceProvider $listen数组中注册您想要操作的事件。

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    'jdavidbakr\MailTracker\Events\EmailSentEvent' => [
        'App\Listeners\EmailSent',
    ],
    'jdavidbakr\MailTracker\Events\ViewEmailEvent' => [
        'App\Listeners\EmailViewed',
    ],
    'jdavidbakr\MailTracker\Events\LinkClickedEvent' => [
        'App\Listeners\EmailLinkClicked',
    ],
    'jdavidbakr\MailTracker\Events\EmailDeliveredEvent' => [
        'App\Listeners\EmailDelivered',
    ],
    'jdavidbakr\MailTracker\Events\ComplaintMessageEvent' => [
        'App\Listeners\EmailComplaint',
    ],
    'jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent' => [
        'App\Listeners\BouncedEmail',
    ],
];

将数据传递给事件监听器

很多时候,您可能需要将发送的电子邮件链接到另一个模型。处理此问题的最佳方法是在您的发出的电子邮件中添加一个标题,您可以在事件监听器中检索它。以下是一个示例:

/**
 * Send an email and do processing on a model with the email
 */
\Mail::send('email.test', [], function ($message) use($email, $subject, $name, $model) {
    $message->from('from@johndoe.com', 'From Name');
    $message->sender('sender@johndoe.com', 'Sender Name');
    $message->to($email, $name);
    $message->subject($subject);

    // Create a custom header that we can later retrieve
    $message->getHeaders()->addTextHeader('X-Model-ID',$model->id);
});

然后在您的事件监听器中:

public function handle(EmailSentEvent $event)
{
    $tracker = $event->sent_email;
    $model_id = $event->sent_email->getHeader('X-Model-ID');
    $model = Model::find($model_id);
    // Perform your tracking/linking tasks on $model knowing the SentEmail object
}

请注意,您附加到电子邮件的标题实际上会与消息一起发送,因此请不要存储任何您不希望向电子邮件收件人公开的数据。

异常

可能会抛出以下异常。您可以将它们添加到异常处理程序中的忽略列表,或者按您希望的任何方式处理它们。

  • jdavidbakr\MailTracker\Exceptions\BadUrlLink - URL链接出现问题。基本上,系统无法正确解析URL链接以发送重定向。

Amazon SES功能

如果您使用Amazon SES,您可以在跟踪中添加一些附加信息。要设置SES回调,首先在SES控制面板中设置您域下的SES通知。然后通过转到通知主题的管理面板并为您从管理页面复制的URL创建订阅来订阅主题。系统应立即响应该订阅请求。如果您喜欢,您可以使用多个订阅(例如,一个用于投递,一个用于退回)。请参阅上面的失败消息触发的事件。为了增加安全性,建议将主题ARN设置到mail-tracker配置中。

视图

当您运行php artisan vendor:publish命令时,简单的视图将添加到您的资源/views/vendor/emailTrakingViews中,您可以根据这些视图进行自定义。当然,您可以使用这些视图作为指南从头开始构建整个管理页面。

管理面板

MailTracker附带内置的管理区域。与包一起发布的默认配置将其放在can:see-sent-emails中间件之后;您可以为该规则创建网关或将其更改为使用您自己的中间件。您还可以更改默认前缀,甚至完全禁用管理路由。

路由名称是'mailTracker_Index'。标准管理面板路由位于/email-manager。您可以使用路由名称将它们包含到现有的管理菜单中。您可以在配置文件中自定义您的路由。您可以看到所有已发送的电子邮件、总打开次数、总URL点击次数、显示单个电子邮件以及显示点击的URL详细信息。

所有视图(电子邮件模板、面板)都可以在resources/views/vendor/emailTrakingViews中进行自定义。

贡献

有关详细信息,请参阅CONTRIBUTINGCONDUCT

安全

如果您发现任何安全相关的问题,请通过电子邮件me@jdavidbaker.com与我联系,而不是使用问题跟踪器。

鸣谢

许可

MIT许可(MIT)。有关更多信息,请参阅许可文件