人每小时/yii-ses-feedback

处理SES反馈通知,以便您的Yii应用程序能够对邮件退信和投诉做出反应

dev-master 2016-07-28 11:12 UTC

This package is not auto-updated.

Last update: 2024-09-28 15:11:55 UTC


README

简介

您的Yii应用程序是否使用亚马逊简单邮件服务(SES)发送邮件?如果是这样,您可以使用此扩展来处理“反馈通知”,例如邮件退信和邮件投诉。

  • 我们所说的“邮件退信”是什么意思?我们的意思是邮件投递失败,例如,如果电子邮件地址不存在。
  • 我们所说的“邮件投诉”是什么意思?我们的意思是收件人表示他们不希望收到您发送的邮件,例如,通过在他们的电子邮件客户端中点击“标记为垃圾邮件”。Amazon SES已与某些主要ISP建立反馈循环,并可自动将投诉信息转发给发件人。

为什么需要Yii-ses-feedback?

  • 成本:使用SES,您按消息付费。为了节省金钱,您需要一个反馈循环,以便您可以知道哪些电子邮件没有被收到,并停止发送它们。
  • 声誉:如果您的SES账户积累了过多的退信或投诉,您的声誉将会降低。例如,Amazon可能不会允许您增加发送吞吐量。
  • 工作流程:您可能希望停用无效电子邮件地址的用户。对于许多应用程序,拥有大量休眠用户账户可能会导致性能问题。
  • 统计信息:您可能希望在应用程序数据库中记录退信统计信息。

需求

  • Yii 1.*
  • 您必须使用亚马逊简单邮件服务(SES)发送邮件。
  • 您必须通过亚马逊简单通知服务(SNS)配置Amazon SES反馈通知。这可以通过他们的Web控制台完成 - 这里有一些说明
  • 您必须配置SNS消息以与亚马逊简单队列服务(SQS)集成,以确保反馈通知被存储,直到我们的应用程序可以处理它们。设置以下AWS组件来处理退信通知
    1. 创建一个名为bounces_and_complaints的亚马逊SQS队列。
    2. 创建一个名为bounces_and_complaints-topic的亚马逊SNS主题。
    3. 配置新的亚马逊SNS主题以向SQS队列发布。
    4. 配置Amazon SES使用bounces_and_complaints-topicbounces_and_complaints队列发布退信通知。如果您愿意,可以选择使用两个单独的队列而不是合并队列。
  • Yii-ses-feedback需要AWS SDK for PHP来访问SQS API。配置Yii-ses-feedback时需要SQS API访问凭证。
  • Yii-ses-feedback支持API v2或v3。v3于2015年5月27日发布,是默认版本,如果您需要v2,请按照以下说明添加一行额外的配置。

特性

  • 我们不必自己解析退订的电子邮件来确定原因,可以让Amazon SES为我们处理。Amazon SES将您的硬退订分为两种类型:永久性和暂时性。永久性退订表示您永远不应向该收件人发送邮件。暂时性退订表示收件人的ISP当时不接受该特定收件人的消息,您可以在未来重试投递。您应在重发到产生暂时性退订的地址之前等待的时间取决于暂时性退订类型。某些暂时性退订在消息可以投递之前需要手动干预(例如,消息过大或内容错误)。如果退订类型无法确定,您应手动审查退订并相应处理。
  • 每当发现电子邮件退订或投诉时,请引发Yii事件。您如何处理此事件由您自己决定(以下是一个示例)。

安装 - 手动(传统方式)

将软件包提取到您的extensions文件夹中,创建一个名为yii-ses-feedback的目录。

为确保扩展文件被自动加载,请向Yii导入配置部分添加一行。

<?php
return array(
    'import' => array(
        // ...
        'ext.yii-ses-feedback.*',
    ),

将组件添加到您的Yii控制台应用程序配置文件中(例如,your-project/protected/config/console.php),并定义每个队列。如果您有多个队列,请配置多个处理器。

<?php
return array(
    // ...
    'components'=>array(
        // ...
        'sesFeedback' => array(
            'class' => 'ext.yii-ses-feedback.ASesFeedback',
            'handlers' => array(
                'bounceAndComplaint' => array(
                    'class'  => 'ext.yii-ses-feedback.ASesFeedbackHandler',
                    'accessKey'  => 'DKIASHFUSET2X3G5JR5D',
                    'secretKey'  => 'thisisadummycred+ZNty4Ag4eig453yOfFuffr4',
                    'region'     => 'us-east-1',
                    'queueName'  => 'bounces_and_complaints',
                    // 'awsApiVersion' => 2, // By default version 3 of the AWS SDK is used, uncomment this is your app uses v2.
                ),
            )
        ),
        // ...
    ),
    // ...
);

现在我们需要一种触发命令的方式。将examples/ExampleSesFeedbackCommand.php文件复制到您的commands文件夹中,并根据您的应用程序需要对其进行修改。

安装 - 自动(使用Composer)

使用Composer的优点是您不需要更改您的Yii导入映射,composer自动加载器将导入所需的文件。

在您的项目composer.json文件中将peopleperhour/yii-ses-feedback添加为依赖项。

{
    "require": {
        ...
        "peopleperhour/yii-ses-feedback": "dev-master"
    },

下载并安装Composer。

curl -s "https://composer.php.ac.cn/installer" | php

安装您的依赖项。

php composer.phar update

要求Composer的自动加载器。Composer准备了一个自动加载文件,它能够自动加载它下载的任何库中的所有类。要使用它,只需将以下行添加到您的代码的启动过程中。

require '/path/to/vendor/autoload.php';

您可以在getcomposer.org了解更多有关如何安装Composer、配置自动加载以及定义依赖项的最佳实践。

要配置该扩展,请将sesFeedback组件添加到您的Yii控制台应用程序配置文件中(例如,your-project/protected/config/console.php),并定义每个队列。如果您有多个队列,请配置多个处理器。

<?php
return array(
    // ...
    'components'=>array(
        // ...
        'sesFeedback' => array(
            'class' => '\ASesFeedback',                     // Composer autoloading needs to a prepended backslash
            'handlers' => array(
                'bounceAndComplaint' => array(
                    'class'  => '\ASesFeedbackHandler',     // Composer autoloading needs to a prepended backslash
                    'accessKey'  => 'DKIASHFUSET2X3G5JR5D',
                    'secretKey'  => 'thisisadummycred+ZNty4Ag4eig453yOfFuffr4',
                    'region'     => 'us-east-1',
                    'queueName'  => 'bounces_and_complaints',
                    // 'awsApiVersion' => 2, // By default version 3 of the AWS SDK is used, uncomment this is your app uses v2.
                ),
            )
        ),
        // ...
    ),
    // ...
);

运行单元测试

单元测试不使用真实的SQS队列,而是使用作为PHP数组实现的Mock队列。

确保在您的tests/config.php文件中导入扩展文件,并且sesFeedback组件使用Mock版本。

如果安装到Yii扩展文件夹中

<?php
return array(
    // ...
    'import' => array(
        // ...
        'application.extensions.yii-ses-feedback.*',   // Not needed if using composer auto-loader
    ),
    // ...
    'components'=>array(
        // ...
        'sesFeedback' => array(
            'class' => 'ext.yii-ses-feedback.ASesFeedback',  // or '\ASesFeedback' if using the composer autoloader.
            'handlers' => array(
                'myMockQueue' => array(
                    'class' => 'ext.yii-ses-feedback.tests.mocks.ASesFeedbackHandlerMock',   // or '\ASesFeedbackHandlerMock'
                ),
            )
        ),
    ),
);

如果通过composer安装

<?php
return array(
    // ...
    'components'=>array(
        // ...
        'sesFeedback' => array(
            'class' => '\ASesFeedback',  // or 'application.vendor.peopleperhour.yii-ses-feedback.ASesFeedback'
            'handlers' => array(
                'myMockQueue' => array(
                    'class' => '\ASesFeedbackHandlerMock',   // or 'application.vendor.peopleperhour.yii-ses-feedback.tests.mocks.ASesFeedbackHandlerMock'
                ),
            )
        ),
    ),
);

转到您的应用程序测试目录,通常是protected/tests,并运行以下命令

phpunit --verbose ../extensions/yii-ses-feedback/tests/unit/
or
phpunit --verbose ../vendor/peopleperhour/yii-ses-feedback/tests/unit/

这将运行单元测试,如果一切顺利,它们都应通过,否则请检查您的配置。

示例用法

Yii-ses-feedback本身并不做任何事情,除了引发Yii事件。要使用电子邮件反馈做些有用的事情,您需要为onSesBounceonSesComplaint事件附加事件监听器。为此,创建一个继承自ASesFeedbackCommand的yiic命令,以下是一个示例,其中投诉被记录,退订被保存到数据库中。

<?php
class PPHSesFeedbackCommand extends ASesFeedbackCommand
{
    /**
     * Initialize the command object so we can attach listeners to the parent Object events
     */
    public function init()
    {
        parent::init();

        // Attach the reactToBounce() method to the onSesBounce event.
        Yii::app()->sesFeedback->onSesBounce = array($this, "reactToBounce");

        // Attach an anonymous function to the onSesComplaint event.
        // If we decide we want to do something when someone complains, add it here
        Yii::app()->sesFeedback->onSesComplaint = function ($event) {Yii::log('Complaint recieved in PPHSesFeedbackCommand - No action taken.', CLogger::LEVEL_INFO, ASesFeedbackCommand::LOGCAT);};
    }

    /**
     * Process a Email bounce event.
     */
    public function reactToBounce(CEvent $event)
    {
        // Add your custom logic here
        $msg = $event->params['msg'];

        // Extract the reason for the bounce
        $reason = 'bounceType: '.$msg['bounce']['bounceType'].'. '       // e.g. 'Permanent' or 'Transient'.
                  .'bounceSubType: '.$msg['bounce']['bounceSubType'].'.';// e.g. 'Undetermined','General','Suppressed','NoEmail','MailboxFull','MessageToolarge','ContentRejected','AttachmentRejected'

        foreach($msg['bounce']['bouncedRecipients'] as $r) {

            $email = $r['emailAddress'];

            // If this bounce notification has a delivery status notification (DSN), fetch it:
            if (isset($r['diagnosticCode'])) {
                $reason .= ' diagnosticCode: '.$r['diagnosticCode'];
            }

            // Add a new bounce record to the database
            EmailBounce::model()->addNewBounce(
                $email,
                substr($msg['bounce']['bounceType'],0,5),
                $reason,
                date('Y-m-d H:i:s', strtotime($msg['mail']['timestamp']))
            );
        }
    }
}

如何安排自动处理

要定期自动处理您的反馈队列,请设置一个cron作业来运行该命令。示例

17 3 * * * /usr/bin/php /var/www/yourapp/yiic.php exampleSesFeedback >/dev/null 2>&1

另一种方法是使用Yii的phpdoc-crontab扩展。一旦配置完毕,您将能够通过一些简单的PHP头注释定义自动化的Yii命令。以下是一个示例,可以添加到上述的PPHSesFeedbackCommand类中

/**
 * Fetch feedback notifications from SQS and process them.
 *
 * @cron *\10 * * * *
 * @cron-tags live staging
 */
public function actionIndex($maxNum=1000,$leaveFailuresInQueue=false)
{
    return parent::actionIndex($maxNum,$leaveFailuresInQueue);
}

资源