remp/ mailer-module
REMP 邮件模块
Requires
- php: ^8.1
- ext-intl: *
- ext-json: *
- ext-pdo: *
- contributte/webpack: ^2.2
- embed/embed: ^4.3
- guzzlehttp/guzzle: ^7.0
- halaxa/json-machine: ^1.1
- justinrainbow/json-schema: ^5.2
- latte/latte: ^3.0
- league/event: ^3.0
- mailgun/mailgun-php: ^4.0
- monolog/monolog: ^3.0
- nette/application: ^3.2
- nette/bootstrap: ^3.0
- nette/caching: ^3.0
- nette/database: ^3.0
- nette/di: ^3.0
- nette/forms: ^3.0
- nette/http: ^3.0
- nette/mail: ^4.0
- nette/robot-loader: ^4.0
- nette/security: ^3.1 <3.2
- nette/utils: ^4.0
- predis/predis: ^1.1
- remp/remp-commons: @dev|^0.32
- robmorgan/phinx: ^0.15
- rootpd/nette-sentry: ^2.0
- symfony/console: ^6.4
- symfony/http-client: ^6.4
- tomaj/hermes: ^4.0
- tomaj/nette-api: ^2.1
- tomaj/nette-bootstrap-form: ^2.1
- tracy/tracy: ^2.6
- twig/intl-extra: ^3.3
- twig/twig: ^3.1
- vlucas/phpdotenv: ^5.2
This package is auto-updated.
Last update: 2024-09-11 08:01:17 UTC
README
邮件模块是 REMP 邮件的核心可扩展包,用作配置邮件工具、创建电子邮件布局和模板以及配置和向选定的用户段发送邮件任务的工具。
安装
使用 composer 安装此包
composer require remp/mailer-module
在 Bootstrap.php
文件(Nette 配置器初始化的地方),将模块的 config.root.neon
文件作为第一个配置文件添加
$configurator = new Nette\Configurator; $configurator->addConfig(__DIR__ . '/../vendor/remp/mailer-module/src/config/config.root.neon'); // ... rest of the configuration
配置
Redis
您可以为默认的 Redis 键前缀进行配置,该前缀在启用 RedisClientTrait
的实现并使用 useRedisKeysPrefix()
方法进行前缀化时使用。
mailer: redis_client_factory: prefix: foo_
您可以在配置中通过调用 useRedisKeysPrefix()
方法来为特定服务(使用 RedisClientTrait
)启用前缀化。
services: mailCache: setup: - useRedisKeysPrefix()
权限管理
您可以通过在配置文件中调用这些设置方法来设置和控制不同角色的权限
services: permissionManager: setup: # creates role `manager` with access to batch (resource) privileges `start` and `stop` - allow('manager', 'batch', ['start', 'stop']) # assigns `manager` role to user with email `manager@example.com` - assignRole('manager@example.com', 'manager')
如果不将权限注册到任何角色,则默认允许每个人,这是为了向后兼容。
允许的权限列表
电子邮件本地化
您可以通过在本地化服务的配置中添加二级区域设置数组来支持其他语言。
services: localizationConfig: setup: - addSecondaryLocales(['cs'])
默认区域设置为使用 .env
变量 LOCALE
。
电子邮件允许列表
邮件支持允许列表电子邮件地址,以防止向不希望收到的电子邮件地址发送电子邮件。此功能默认不激活。要激活允许列表,您必须在应用程序配置中指定一个或多个允许的电子邮件地址。
services: emailAllowList: setup: - allow("example1@example.com") - allow("example2@example.com")
要允许指定域中的所有电子邮件地址,您必须使用双 @,因为 Nette 将单个 @ 作为对其他服务的引用处理。
services: emailAllowList: setup: - allow("@@example.com") - allow("@@example2.com")
允许域管理器
邮件向外出站电子邮件中找到的链接添加 RTM 参数。要控制向哪些链接添加 RTM 参数,您必须指定允许的域。只有与允许域的所有域级别匹配的链接才会添加 RTM 参数。
services: allowedDomainManager: setup: - addDomain('dennikn.sk')
电子邮件模板服务参数
邮件允许在邮件模板中添加服务参数,这些参数可在每封电子邮件中使用。要绑定自己的参数,您必须在配置中注册 ServiceParamsProviderInterface
的实现,并在其中添加自己的参数。
注册示例
services: serviceParamsProvider: factory: App\MyServiceParamsProvider
邮件模块默认在 DefaultServiceParamsProvider
中添加了两个参数(settings
和 unsubscribe
)。如果注册了自己的服务参数提供程序,则默认参数将不会使用。您可以通过扩展 DefaultServiceParamsProvider
来替代实现 ServiceParamsProviderInterface
,以保留绑定默认服务参数。
技术特性描述
邮件设置
您可以设置多个邮件发送。在 addMailer
函数中使用 code
(见示例)来区分同一类型的邮件。在设置中,您必须为每个邮件提供必要的值。
-
多个邮件设置示例
mailFactory: setup: - addMailer(Remp\MailerModule\Models\Mailer\MailgunMailer(code: us)) - addMailer(Remp\MailerModule\Models\Mailer\MailgunMailer(code: eu))
新闻通讯列表
列表代表电子邮件类别(新闻通讯)。它们的主要(也是唯一)用途是在新闻通讯内分组单个电子邮件并管理用户对这些新闻通讯的订阅。
当您创建新的新闻通讯时,您可以指定
- 名称。 新闻通讯的用户友好名称(例如,“每周通讯”,“突发新闻”等)
- 代码。 新闻通讯的计算机友好名称(缩写;例如,“weekly_newsletter”,“breaking_news”,等)
- 自动订阅标志。 标志表示新用户(通过API由外部CRM报告)是否应自动订阅新闻通讯。
- 公开标志。 标志表示新闻通讯是否应列在面向最终用户的公开列表中。邮件发送器不提供面向用户的界面,因此此标志主要针对实施前端的一方。
最终用户的订阅/取消订阅处理均通过API进行。出版商通常希望将其包含在系统客户区中,与系统的其余部分保持一致。因此,邮件发送器不提供任何面向用户的界面。
要查看集成订阅管理的API,请参阅管理用户订阅部分。
电子邮件和布局
电子邮件和布局提供了一种手动创建电子邮件的方法,同时可以在创建过程中实时预览电子邮件。正如其名称所暗示的
-
布局。 它们是电子邮件中的常见和可重用部分。通常是标题(包含标志和欢迎信息)和页脚(包含您的凭据和取消订阅链接)。
要正确生成电子邮件,请将
{{ content|raw }}
放置在应注入实际电子邮件内容的位置。 -
电子邮件。 它代表电子邮件的实际内容(例如,每周通讯的单期)。每个电子邮件都有一些您可以配置的设置
- 名称。 电子邮件的用户友好名称。它仅在系统的管理部分中显示。
- 代码。 电子邮件的计算机友好名称(缩写)。主要在引用手动发送的单个电子邮件时使用。
- 描述。 内部描述,以便您在一年后知道电子邮件的目的。
- 布局。 要使用的布局。
- 新闻通讯列表。 此电子邮件所属的新闻通讯(类别)。在将电子邮件发送给特定最终用户之前,邮件发送器会检查用户是否已订阅此新闻通讯。如果没有,则不会发送电子邮件。
- 发件人。 应用作电子邮件发送者的名称(例如,
Support <support@example.com
)。 - 主题。 电子邮件主题。
- 文本版本。 电子邮件客户端用作回退的文本版本。
- HTML版本。 人们将看到的HTML(主要)电子邮件版本。HTML版本将在创建新电子邮件的表单中显示预览。
HTML版本可以通过CodeMirror编辑器(html,默认)或Trumbowyg编辑器(所见即所得)进行编辑。默认编辑器可以使用.env
变量TEMPLATE_EDITOR
进行更改。(选项:codemirror
,wysiwyg
)
电子邮件的文本和HTML版本支持Twig语法,您可以在模板中使用标准的Twig功能。邮件发送器能够为您的模板提供自定义变量。这些变量可以来自不同的来源
-
系统变量。
-
autologin
:为每个电子邮件地址生成并打印唯一的令牌,该令牌可以通过users/check-token API进行验证。它旨在用于URL中(例如,
http://dennikn.sk/email-settings{{ autologin }}
)
-
-
由生成器提供的变量,只能用于生成器模板。如果您的生成器提供
foo
变量,您可以在生成器模板中使用它作为{{ foo }}
。 -
由
IUser
提供的变量(见用户集成)。例如,如果您的API响应包括用户集成示例中描述的first_name
键,您可以在电子邮件模板中使用它作为{{ first_name }}
变量。
注意:Mailer不会验证变量的存在,也不会提供默认值。如果你使用了自定义变量,而在IUser
响应中不存在,空字符串将被注入到你的电子邮件正文中。
保存电子邮件不会触发任何发送。它创建了一个电子邮件实例,该实例可以手动(或由第三方)通过API或作为Mailer的作业内的批次发送。
作业
作业可以理解为新闻邮件发送订单。它们可以由提供自己统计信息的更小的批次组成。当你想在较小的批次上运行A/B测试,评估并发送电子邮件给剩余用户时,这很有用。
创建一个作业时,你隐式地创建它的第一个批次。作业只有一个选项在所有批次中共享
- 分段。定义了哪些分段用户(需要集成)应该收到电子邮件。这与任何新闻邮件订阅者无关。用户分段应简单地说明你正在针对的用户集。例如,“昨天注册的人”或“没有付款的人”。这是在作业级别配置的,并且在所有批次中共享。
所有其他选项都与与作业一起创建的批次相关。之后,如果需要,你可以在作业内创建更多批次。
- 方法。指定由分段提供的电子邮件列表是否应该随机化,或者电子邮件应该按照在分段中返回的顺序发送。
- 电子邮件A。要发送的主要电子邮件(隐式指示将用于检查用户是否可以接收电子邮件的新闻邮件列表)。
- 电子邮件B。如果您想在批次中包含A/B测试,您可以指定电子邮件的另一个变体。变体的分布将在所有变体之间均匀分配。
- 电子邮件数量。限制批次内要发送的电子邮件数量。
- 开始日期。指定批次应发送的时间(现在或未来)。
当创建作业/批次时,您需要点击“开始发送”或“准备队列”按钮。点击这两个按钮后,后台处理器将接收有关目标用户组的必要信息,并为发送守护进程准备元数据。
当元数据准备好后,当按下“开始发送”按钮时,批次将被发送守护进程选中,并通过预配置的Mailer(SMTP、Mailgun等)发送实际的电子邮件。当按下“准备队列”按钮时,批次处于处理状态,所有元数据都已准备就绪,并且可以知道将实际发送的电子邮件数量。
您可以通过使用提供的API端点以编程方式创建和执行作业/批次。
要删除处于处理状态的旧批次,请使用mail:remove-old-batches
命令。当批次在处理
状态持续超过24小时时,被视为旧批次。当状态更改为处理
时准备的所有元数据都可能过时,因此不建议用于发送电子邮件。
生成器
生成器是单用途实现,可以帮助根据提供的输入以编程方式生成电子邮件的HTML/文本内容。这意味着,在每次想要发送电子邮件时,而不是手动准备电子邮件内容(要发送的HTML),您可以简化创建过程,通过实现自定义生成器来执行艰苦的工作。
例如,生成器可能需要您文章的URL列表。当它获取到这些URL后,它会解析URL的内容,提取文章的标题、摘录和图片,并将其注入到准备好的生成器模板中。准备电子邮件的人可以保证不会创建无效的HTML(由于输入错误),整个流程由于只需要输入文章URL,因此速度会加快。我们刚刚描述的流程与ArticleUrlParserGenerator
的工作方式相符。
每个准备好的生成器模板都直接与一个生成器实现相关联。因此,可以保证生成器模板中使用的变量始终会提供(除非实现中存在错误)。
实现生成器
要创建一个新的生成器,您需要实现Remp\MailerModule\Generators\IGenerator
接口。以下是对方法的描述,并参考了UrlParserGenerator
-
generateForm(Form $form)
。生成器需要一种从用户获取任意输入的方法。此方法应向$form
实例添加新的表单元素,并声明验证规则。class UrlParserGenerator implements IGenerator { // ... public function generateForm(Form $form) { // ... $form->addTextArea('articles', 'Article') ->setAttribute('rows', 7) ->setOption('description', 'Paste article URLs. Each on separate line.') ->getControlPrototype() ->setAttribute('class', 'form-control html-editor'); // ... }
-
onSubmit(callable $onSubmit)
。待办事项:将其隐藏到抽象类中? -
process($values)
。处理由API或生成器表单提供的输入值,并生成包含htmlContent
和textContent
属性的输出。这些属性的值应用作电子邮件的HTML/文本内容。处理可能包括文本替换、从第三方服务获取数据以及任何有助于塑造最终电子邮件HTML的内容。class UrlParserGenerator implements IGenerator { // ... public function process($values) { $sourceTemplate = $this->sourceTemplatesRepository->find($values->source_template_id); $items = []; $urls = explode("\n", trim($values->articles)); foreach ($urls as $url) { $url = trim($url); $meta = $this->content->fetchUrlMeta($url); if ($meta) { $items[$url] = $meta; } } $params = [ 'intro' => $values->intro, 'footer' => $values->footer, 'items' => $items, 'rtm_campaign' => $values->rtm_campaign, ]; $engine = $this->engineFactory->engine(); return [ 'htmlContent' => $engine->render($sourceTemplate->content_html, $params), 'textContent' => strip_tags($engine->render($sourceTemplate->content_text, $params)), ]; } }
-
getWidgets()
。提供一个数组,其中包含可能在表单提交后渲染在页面上的小部件的类名。由于生成器“仅”生成电子邮件的HTML/文本内容,您可能希望在成功页面上附加一些额外的行为或控件,例如电子邮件预览或创建并开始作业/批处理的按钮。例如,请参阅NewsfilterGenerator的返回值以及NewsfilterWidget的实现,该实现预览提供的电子邮件HTML内容并渲染额外的表单,以提供创建电子邮件和作业/批处理所需的数据。
class NewsfilterGenerator implements IGenerator { // ... public function getWidgets() { return [NewsfilterWidget::class]; } }
-
apiParams()
。提供生成器在使用API时所需的输入参数数组。这些参数应与在generateForm()
方法中添加的字段相匹配。它们在调用Generate mail API时使用。class UrlParserGenerator implements IGenerator { // ... public function apiParams() { return [ new InputParam(InputParam::TYPE_POST, 'source_template_id', InputParam::REQUIRED), new InputParam(InputParam::TYPE_POST, 'articles', InputParam::REQUIRED), new InputParam(InputParam::TYPE_POST, 'footer', InputParam::REQUIRED), new InputParam(InputParam::TYPE_POST, 'rtm_campaign', InputParam::REQUIRED), new InputParam(InputParam::TYPE_POST, 'intro', InputParam::REQUIRED) ]; } }
-
preprocessParameters()
。接收由第三方集成(例如Wordpress帖子数据)提供的数据,并将其映射到apiParams()
中声明的参数。这是一种非常具体的集成使用,可以按以下方式使用:- 您的CMS(例如Wordpress)包含一个集成,该集成声明特定类别的帖子可以使用Mailer的生成器。该集成直接声明可以使用的生成器模板。
- CMS通过POST数据调用Mailer的preprocess API,数据中包含Wordpress帖子。生成器将Wordpress帖子数据映射到它作为输入所需的字段。这意味着不会在Wordpress端建立硬连接。
- 由于生成器实现可能非常具体(例如,我们的一些生成器期望Wordpress帖子的文本版本作为输入之一),因此将生成器的API部分与调用者绑定是可以接受的。
- 内容管理系统(CMS)以表单的形式接收从WordPress帖子中提取的数据,这些数据可以提交给邮件发送器的生成器表单,以及可以提交这些数据的URL。
- CMS提供一个链接,该链接创建一个隐藏表单,用预处理的数据填充它,并将其提交给邮件发送器。
结果是,用户不需要手动将数据从WordPress复制粘贴到邮件发送器,只需简单“触发”电子邮件生成,然后重定向到邮件发送器的生成器成功页面。请参阅NewsfilterGenerator以获取参考实现。
class NewsfilterGenerator implements IGenerator { // ... public function preprocessParameters($data) { $output = new \stdClass(); if (!isset($data->post_authors[0]->display_name)) { throw new PreprocessException("WP json object does not contain required attribute 'post_authors.0.display_name'"); } $output->editor = $data->post_authors[0]->display_name; $output->from = "Denník N <info@dennikn.sk>"; foreach ($data->post_authors as $author) { if ($author->user_email === "editori@dennikn.sk") { continue; } $output->editor = $author->display_name; $output->from = $author->display_name . ' <' . $author->user_email . '>'; break; } if (!isset($data->post_title)) { throw new PreprocessException("WP json object does not contain required attribute 'post_title'"); } $output->title = $data->post_title; if (!isset($data->post_url)) { throw new PreprocessException("WP json object does not contain required attribute 'post_url'"); } $output->url = $data->post_url; if (!isset($data->post_excerpt)) { throw new PreprocessException("WP json object does not contain required attribute 'post_excerpt'"); } $output->summary = $data->post_excerpt; if (!isset($data->post_content)) { throw new PreprocessException("WP json object does not contain required attribute 'post_content'"); } $output->newsfilter_html = $data->post_content; return $output; } }
注册生成器
当您的实现准备就绪时,请在config.local.neon
中注册您的生成器。registerGenerator
方法的参数如下:
- type:生成器的URL友好名称,用于将生成器模板与实际实现类链接。如果移除仍然在生成器模板中使用的type,可能会导致系统不一致和错误。
- label:生成器的名称,如显示在邮件发送器管理表单中。
- instance of
Remp\MailerModule\Generators\IGenerator
:当选择生成器时使用的实现类。由于type用于引用/链接生成器模板和生成器实现,因此可以在任何时候安全地交换实现实例。
services: # ... generator: setup: - registerGenerator('newsfilter', 'Newsfilter', Remp\MailerModule\Generators\NewsfilterGenerator())
动作基本流程
以下图中显示了邮件发送器如何工作的简化视图。
与用户基础集成
邮件发送器依赖于外部用户基础和段提供者。安装后,应用程序使用模拟实现Remp\MailerModule\Segment\Dummy
和Remp\MailerModule\User\Dummy
。
要集成邮件发送器,您需要提供这些接口的真实实现,以针对您的系统(负责保存用户信息)负责。API定义可以是任何适合您的定义,但最终实现必须处理响应并返回以下结构的数据。
段集成
为了确定向谁发送电子邮件,邮件发送器依赖于用户段——实际上是应接收通讯录的用户ID列表。您可以注册任意数量的段提供者,唯一的条件是提供者应该使用相同的用户基础(一个用户ID必须始终指向)同一个用户。
实现需要实现Remp\MailerModule\Models\Segment\ISegment
接口。
需要实现三种方法
-
provider(): string
:在段提供者中唯一标识段提供者。这在内部需要命名空间段名称,以防多个段源中使用了相同的段名称。return "my-provider";
-
list(): array
:返回此提供者可用的所有段的列表。响应结构为return [ [ 'name' => String, // user friendly label 'provider' => String, // should be same as result of provider() 'code' => String, // machine friendly name, slug 'group' => [ 'id' => Integer, // ID of segment group 'name' => String, // user friendly label of group 'sorting' => Integer // sorting index; lower the number, sooner the group appears in the list ] ], ];
-
users($segment): array
:返回属于段的用户ID列表。$segment
:请求段的标识。
[ 'provider' => String, // identification of segment provider 'code' => String, // segment code ]
响应预期将是表示用户ID的整数/字符串数组
return [ Integer, Integer, // ... ]
模拟实现
请参阅Remp\MailerModule\Models\Segment\Dummy
实现作为参考示例。
REMP CRM实现
请参阅Remp\MailerModule\Models\Segment\Crm
实现,以检查如何初始化类依赖关系、构建请求和处理结果
构造函数接受两个参数。它们应来自app/config/config.local.neon
文件
parameters: crm: addr: @environmentConfig::get('CRM_ADDR') api_token: @environmentConfig::get('CRM_API_TOKEN') services: segmentAgreggator: setup: # add your implementation - register(Remp\MailerModule\Segment\Crm(%crm.addr%, %crm.api_token%))
用户集成
由于段仅使用用户ID,其中一些可能不再有效或活跃,邮件发送器需要实现根据ID返回用户信息的功能。
实现需要实现 Remp\MailerModule\Models\Users\IUser
接口。
-
list($userIds, $page): array
:根据提供的用户ID和分页参数返回请求用户的用户信息(主要是电子邮件地址)。您方面的分页实现不是强制性的,但强烈推荐。$userIds
:[String|Integer, String|Integer, ...]
// 用户ID列表;空数组应处理为对所有用户的请求。$page
:Integer
:当前请求的页码。
预期的响应如下
return [ $userId => [ 'id' => String, // userId 'email' => String, // valid email address of user // you can provide optional data that can be used within your email templates, for example: 'first_name' => String, 'last_name' => String, ], ];
模拟实现
请参考 Remp\MailerModule\Models\Users\Dummy
实现作为参考示例。
REMP CRM实现
请参考 Remp\MailerModule\Models\Users\Crm
实现以检查您如何初始化类依赖项,构建请求并处理结果
构造函数接受两个参数。它们应来自app/config/config.local.neon
文件
parameters: crm: addr: @environmentConfig::get('CRM_ADDR') api_token: @environmentConfig::get('CRM_API_TOKEN') services: # add your implementation - Remp\MailerModule\User\Crm(%crm.addr%, %crm.api_token%)
然后将响应作为过程来匹配预期的结构
$response = $this->client->post(self::ENDPOINT_LIST, [ 'form_params' => [ 'user_ids' => Json::encode($userIds), 'page' => $page, ], ]); $result = Json::decode($response->getBody(), Json::FORCE_ARRAY);
管理用户订阅
邮件发送器保留有关哪个用户订阅了哪些新闻通讯的信息,并提供
- API来处理更改(如果您能够从用户基础中调用API)
- 命令来获取更改(如果您能够为邮件发送器创建一个可以调用的API)
邮件发送器感兴趣的变化是
-
用户注册。邮件发送器自动将用户订阅到所有启用了
auto_subscribe
标志的新闻通讯。 -
电子邮件更改。邮件发送器还将订阅信息保留到电子邮件地址。当用户更改其电子邮件时,邮件发送器需要更新该信息。
-
新闻通讯订阅和取消订阅。邮件发送器不提供订阅和取消订阅新闻通讯的前端界面 - 网站所有者往往会将其集成到他们的布局中。由于这一点,邮件发送器提供了订阅和取消订阅用户的新闻通讯的API。
如果您无法调用这些API,您可以创建控制台命令,并使用您的更新逻辑与您的API同步数据。
如果订阅更改不是从CRM触发的,建议通知CRM有关用户设置的更改。您可以使用Hermes工作进程 \Remp\MailerModule\Hermes\NotifyCrmSubscribeUnsubscribeHandler
通知CRM更改并刷新用户缓存的本地数据。要启用此功能,请将处理程序注册到您的 config.local.neon
services: hermesWorker: setup: - add('user-subscribed', Remp\MailerModule\Hermes\NotifyCrmSubscribeUnsubscribeHandler()) - add('user-unsubscribed', Remp\MailerModule\Hermes\NotifyCrmSubscribeUnsubscribeHandler()) - add('user-subscribed-variant', Remp\MailerModule\Hermes\NotifyCrmSubscribeUnsubscribeHandler()) - add('user-unsubscribed-variant', Remp\MailerModule\Hermes\NotifyCrmSubscribeUnsubscribeHandler())
邮件发送器
默认情况下,应用程序包含以下实现
您可以在设置页面选择默认的邮件发送器: http://mailer.remp.press/settings/
邮件发送器集成
您可以将自己的邮件发送器实现添加到您选择的服务。
实现需要扩展 Remp\MailerModule\Models\Mailer
抽象类。
-
protected $alias = String
:用于实现识别的类属性,仅用于日志记录目的 -
protected $options = [ String, String, ... ]
:用于定义选项的类属性,这些选项应可通过邮件发送器设置页面进行配置 -
supportsBatch(): bool
:返回标志,指示实现是否支持批量发送或每封电子邮件都应单独发送 -
transformTemplateParams($params)
:邮件发送器支持通过在模板中使用{{ variable }}
来注入变量。一些电子邮件服务要求在电子邮件模板中使用特定变量以支持批量发送。这些变量的值通常在发送API请求时提供,第三方服务在发送前注入它们。这意味着注入不能由邮件发送器完成,而必须传递给第三方服务。
此方法应替换邮件模板中的此类变量,以便第三方可以正确替换它们。
$params
:基于字符串的键值对,包含单个电子邮件的值。
期望返回两个数组。
- 用于第三方替换的具有通用模板变量的转换参数。
- 将作为值注入第三方服务的键值对(可能已更改)。
例如,对于Mailgun,在输入时接收
$params
。[ "autologin_token": "foo", "mail_sender_id": "baz", ]
并输出两个数组。
- 转换参数
[ "autologin_token": "%recipient.autologin_token%", "mail_sender_id": "%recipient.mail_sender_id", ]
- 键值对
[ "autologin_token": "foo", "mail_sender_id": "baz", ]
-
send(Message $message)
:发送电子邮件的实际实现。$message
对象提供了发送电子邮件所需的一切。$message->getFrom()
:发送者键值对(email-name)。$message->getHeader('To')
:接收者键值对(email-name)。$message->getSubject()
:电子邮件主题。$message->getBody()
:文本正文。$message->getHtmlBody()
:HTML正文。$message->getAttachments()
:可用的附件。$message->getHeader('X-Mailer-Tag')
:邮件模板代码(特定电子邮件的slug标识符)。$message->getHeader('X-Mailer-Template-Params')
:由第三方注入的模板变量值。$message->getHeader('X-Mailer-Variables')
:用于实现的电子邮件相关元数据。
注意:SmtpMailer实现自动在发送前删除X-Mailer-Template-Params
标头,以防止敏感信息泄露。
事件接收webhooks
如果您能够配置第三方服务通过webhooks发送电子邮件统计信息,您可以创建一个API处理程序来接收统计信息并进行处理。
我们的Mailgun webhook实现验证请求并将事件标记为稍后异步处理。
如果有多个mailgun邮件发送器,每个webhook URL应在查询参数中指定邮件发送器代码。这与您的config.local.neon
中指定的代码相同。
例如,您应使用webhook URL https://mailer.remp.press/api/v2/mailers/mailgun?code=us
来处理此配置定义的邮件发送器发送的电子邮件的事件。
services: mailFactory: setup: - addMailer(Remp\MailerModule\Models\Mailer\MailgunMailer(code: us))
- API处理程序:
Remp\MailerModule\Api\v2\Handlers\Mailers\MailgunEventsHandler
:注意在HermesMessage
构造函数中的事件类型。它必须与下面配置中的相同。 - 后台事件处理:Remp\MailerModule\Hermes\MailgunEventHandler
要添加自己的API处理程序和后台事件处理,创建实现并将它们注册到config.local.neon
文件中。
services: # ... apiDecider: setup: - addApiHandler(\Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'mailers', 'custom-mailer'), \Remp\MailerModule\Api\v2\Handlers\Mailers\CustomMailerHandler(), \Tomaj\NetteApi\Authorization\NoAuthorization()) hermesWorker: setup: - add('custom-mailer-event', Remp\MailerModule\Hermes\MailgunEventHandler())
事件获取命令
如果您能够通过API从第三方服务获取事件统计信息,我们建议编写一个可以作为守护进程运行的命令行工具,以在生成统计信息时获取统计信息。
根据我们的经验,我们发现webhooks速度更快、更准确,但它们可能会在发送通讯时增加服务器的负载。
我们的Mailgun事件API实现作为守护进程运行,每15秒获取一次新数据。
然后需要将您的实现添加到config.local.neon
文件中。
services: # ... console: setup: - add(Remp\MailerModule\Commands\MailgunEventsCommand())
一旦准备就绪,您可以通过调用 php bin/command.php mailgun:events
来执行它。命令名称(mailgun:events
)是在您的实现中定义的,您可以使用任何命名空间和名称。如果有多个Mailgun邮件发送器,您可以将选项 code
设置为为特定邮件发送器设置命令。
工作进程
为了使应用程序正常运行,您需要运行几个后端工作进程来处理与电子邮件发送相关的任务。
要列出所有可用的控制台命令,请运行 php bin/command.php
。
我们建议使用Systemd或Supervisord来处理它们。以下是一些Systemd配置。
后台事件处理(Hermes工作进程)
此配置设置应用程序生成的所有异步事件的处理器。
在 /etc/systemd/system/remp-mailer-hermes-worker.service
中创建systemd服务定义。修改 ExecStart
行以反映您的安装路径。
# BEGIN remp-mailer-hermes-worker
[Unit]
Description="REMP Mailer Hermes worker"
After=network.target
[Service]
Type=simple
UMask=0022
LimitNOFILE=1024
ExecStart=/usr/bin/sudo -u remp php /home/remp/workspace/remp/Mailer/bin/command.php worker:hermes
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
# END remp-mailer-hermes-worker
现在启用并启动服务
sudo systemctl enable remp-mailer-hermes-worker
sudo systemctl start remp-mailer-hermes-worker
邮件发送(邮件工作进程)
此配置设置负责通过配置的邮件发送器实际发送电子邮件的处理器。
在 /etc/systemd/system/remp-mailer-mail-worker.service
中创建systemd服务定义。修改 ExecStart
行以反映您的安装路径。
# BEGIN remp-mailer-mail-worker
[Unit]
Description="REMP Mailer Mail worker"
After=network.target
[Service]
Type=simple
UMask=0022
LimitNOFILE=1024
ExecStart=/usr/bin/sudo -u remp php /home/remp/workspace/remp/Mailer/bin/command.php worker:mail
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
# END remp-mailer-mail-worker
现在启用并启动服务
sudo systemctl enable remp-mailer-mail-worker
sudo systemctl start remp-mailer-mail-worker
计划事件
邮件作业预处理
一旦您触发了要发送的邮件作业,在发送电子邮件之前需要做一些预处理。
邮件发送器获取属于目标段的用户ID及其电子邮件地址的列表。它还删除所有可能收不到邮件的用户(他们可能已取消订阅),在A/B测试的情况下,将特定的邮件模板分配给特定的电子邮件,以便发送工作进程不需要进行任何繁重的操作。
将以下块添加到您的crontab中以执行处理(根据您的安装路径进行修改)
* * * * * php /home/remp/workspace/remp/Mailer/bin/command.php mail:process-job
邮件统计处理
如果默认的邮件发送器支持统计信息(例如,Mailgun)并且正在接收统计信息,您可以通过启用统计信息聚合来显示它们,以便在作业详情中直接显示。
* * * * * php /home/remp/workspace/remp/Mailer/bin/command.php mail:job-stats
身份验证
默认实现通过REMP SSO进行身份验证。
然而,开始的最简单方法是使用预定义电子邮件和密码列表的SimpleAuthenticator
。它不需要外部身份验证服务。
简单身份验证器
要安装 SimpleAuthenticator
,请将以下内容添加到您的 config.local.neon
(services
部分)中
services: authenticator: factory: Remp\MailerModule\Models\Auth\SimpleAuthenticator setup: - addUser('admin@admin.sk', 'admin_passphrase')
为要添加的每个有效的电子邮件密码组合调用 addUser()
。
自定义身份验证
邮件发送器可以使用外部身份验证,无需安装SSO。
要替换REMP SSO并使用您自己的自定义身份验证,您需要
-
实现
\Nette\Security\IAuthenticator
接口-
authenticate(array $credentials): \Nette\Security\Identity
:接收凭据的方法,根据您想要使用的任何来源进行验证(例如,您的自己的API),并返回\Nette\Security\Identity
的实例。$credentials
:包含凭据的数组,其中$credentials[0]
是用户名,$credentials[1]
是密码。
-
-
实现
\Remp\MailerModule\Auth\BearerTokenRepositoryInterface
接口-
validToken(string $token): boolean
:接收API令牌(从Authorization
标头中读取)的方法,根据您的实现返回它是否有效。 -
ipRestrictions(): string
:返回应为给定令牌白名单的任何IP地址的方法。如果没有限制,则返回*
。
-
最后一步是将这些新实现添加到 config.local.neon
中。请参阅以下部分以读取基于与REMP CRM集成的示例,并用您的实现替换类。
REMP CRM集成
查看 Remp\MailerModule\Models\Auth\Authenticator
的实现和 Remp\MailerModule\Models\Auth\RemoteBearerTokenRepository
的实现,以了解如何初始化您的类依赖、结构化请求和处理结果。
以下代码片段需要添加到您的 config.local.neon
中以启用与 CRM 的集成。代码片段中的类使用 REMP CRM 进行用户身份验证和 API 密钥。
services: # user authentication authenticator: factory: Remp\MailerModule\Models\Auth\Authenticator security.userStorage: factory: Nette\Bridges\SecurityHttp\SessionStorage # api authentication apiTokenRepository: factory: Remp\MailerModule\Models\Auth\RemoteBearerTokenRepository(%crm.addr%)
您在这里覆盖了两个服务,使用自己的实现。第三个服务使用默认的 Nette 实现,并覆盖了在 config.neon
中定义的 REMP SSO 自定义实现。
从现在起,身份验证不再是通过将用户重定向到 SSO 来完成的,而是使用默认的登录屏幕,该屏幕可在 http://mailer.remp.press/sign/in 找到。
错误跟踪
Mailer 随附支持跟踪错误到 Sentry 的扩展。您可以通过将以下代码片段设置到您的 app/config/config.local.neon
中来启用跟踪
extensions: sentry: Rootpd\NetteSentry\DI\SentryExtension sentry: dsn: https://0123456789abcdef0123456789abcdef@sentry.example.com/1 environment: development user_fields: - email
请注意,只有在禁用调试模式的情况下,跟踪才会工作。默认情况下,只有当 ENV
设置为 local
时,调试模式才会启用。
健康检查
路由 http://mailer.remp.press/health
为数据库、Redis、存储和日志提供健康检查。
返回
-
200 OK 和包含服务列表(状态为 "OK")的 JSON。
-
500 内部服务器错误 和包含问题描述的 JSON。例如。
{ "status":"PROBLEM", "log":{ "status":"PROBLEM", "message":"Could not write to log file", "context": // error or thrown exception... //... }
数据库表迁移
由于需要更改主键(int
-> bigint
),在包含大量数据(或具有主键溢出风险的)的表中,我们必须创建迁移过程。由于某些表非常暴露,无法锁定超过几秒钟,我们决定创建新表,手动迁移数据,并在迁移过程中保持旧表和新表同步。
此迁移过程仅适用于特定版本的特定表之后的安装,并且是两步过程。
邮件日志迁移(版本 < 1.1.0)
包括 mail_logs
和 mail_log_conversions
表的迁移。还包括向 mail_logs
表添加 user_id
列。
步骤
- 运行 phinx 迁移
CreateNewMailLogsAndMailConversionsTable
- 创建新表mail_logs_v2
和mail_log_conversions_v2
(如果表中没有数据,迁移仅更改主键类型,后续步骤不需要) - 运行命令
mail:migrate-mail-logs-and-conversions
,将数据从旧表复制到新表(例如,从mail_logs
复制到mail_logs_v2
)- 命令在成功迁移后将原子性地重命名表(例如,mail_logs
->mail_logs_old
和mail_logs_v2
->mail_logs
),因此迁移完成后仅使用新表
建议按顺序运行
mail:bigint_migration_cleanup mail_log_conversions
mail:bigint_migration_cleanup mail_logs
命令,至少在成功迁移后 2 周内(为了保留备份数据,以防出现任何问题)删除多余的表。
用户订阅迁移(版本 < 1.2.0)
包括 mail_user_subscriptions
和 mail_user_subscription_variants
表的迁移。
步骤
- 运行 phinx 迁移
CreateNewMailUserSubscriptionsAndMailUserSubscriptionVariantsTables
- 创建新表mail_user_subscriptions_v2
和mail_user_subscription_variants_v2
(如果表中没有数据,迁移仅更改主键类型,后续步骤不需要) - 执行命令
mail:migrate-user-subscriptions-and-variants
,该命令将数据从旧表复制到新表(例如mail_user_subscriptions
到mail_user_subscriptions_v2
)- 在成功迁移后,命令将原子性地重命名表(例如mail_user_subscriptions
->mail_user_subscriptions_old
和mail_user_subscriptions_v2
->mail_user_subscriptions
),以便迁移结束后仅使用新表。
建议按顺序运行
mail:bigint_migration_cleanup mail_user_subscription_variants
mail:bigint_migration_cleanup mail_user_subscriptions
命令,至少在成功迁移后 2 周内(为了保留备份数据,以防出现任何问题)删除多余的表。
自动登录令牌迁移(版本 < 1.3.0)
包括 autologin_tokens
表迁移。
步骤
- 运行 phinx 迁移
CreateNewAutologinTokensTable
- 创建新表autologin_tokens_v2
(如果表中没有数据,迁移只会更改主键的类型,接下来的步骤不需要执行) - 运行命令
mail:migrate-autologin-tokens
,该命令将数据从旧表复制到新表(autologin_tokens
到autologin_tokens_v2
)- 在成功迁移后,命令将原子性地重命名表(autologin_tokens
->autologin_tokens_old
和autologin_tokens_v2
->autologin_tokens
),以便迁移结束后仅使用新表
建议在成功迁移后至少运行 mail:bigint_migration_cleanup autologin_tokens
命令 2 周(以保留备份数据,以防出现任何问题),然后删除遗留的表。
API 文档
所有示例都使用 http://mailer.remp.press
作为基本域名。请在执行示例之前将主机更改为您使用的域名。
所有示例都使用 XXX
作为默认授权令牌值,请将其替换为在 REMP SSO 中获取的实际 API 令牌。
API 响应可以包含以下 HTTP 状态码
如果可能,响应将包括 application/json
编码的负载,其中包含解释错误的进一步消息。
POST /api/v1/users/user-registered
当用户注册时,Mailer 应该收到通知,以便它可以开始跟踪新电子邮件地址的时事通讯订阅。此新电子邮件地址将自动订阅已启用 自动订阅 选项的任何时事通讯。
头部
参数
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/user-registered \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'email=admin%40example.com&user_id=12345'
响应
{ "status": "ok" }
POST /api/v1/users/bulk-user-registered
类似于之前的 API users/user-registerd
。订阅多个提供的用户。
当用户注册时,Mailer 应该收到通知,以便它可以开始跟踪新电子邮件地址的时事通讯订阅。此新电子邮件地址将自动订阅已启用 自动订阅 选项的任何时事通讯。
头部
主体
{ "users": [ { "email": "admin@example.com", "user_id": "12345" }, { "email": "test@example.com", "user_id": "67890" } ] }
一个用户的属性
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/bulk-user-registered \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "users": [ { "email": "admin@example.com", "user_id": 12345 }, { "email": "test@example.com", "user_id": "67890" } ] }'
响应
{ "status": "ok" }
错误示例
curl -X POST \ http://mailer.remp.press/api/v1/users/bulk-user-registered \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "users": [ { "email": "admin@example.com" }, { "user_id": "67890" }, { "email": "qa@example.com", "user_id": "qa123" } ] }'
响应
{ "status": "error", "message": "Input data contains errors. See included list of errors.", "errors": { "element_0": "Required field missing: user_id.", "element_1": "Required field missing: email.", "element_2": "Invalid field: 'user_id' must be integer. Got [qa123]." } }
POST /api/v1/users/is-unsubscribed
API 调用,检查用户是否已从给定的时事通讯列表中取消订阅。
头部
主体
{ // required "user_id": 1, // Integer; ID of user "email": "test@test.sk", // String; Email of user, "list_id": 1 // Integer; ID of newsletter }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/is-unsubscribed \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "user_id": 123, "email": "test@test.sk", "list_id": 1 }'
响应
{ "is_unsubscribed": true }
POST /api/v1/users/is-subscribed
API 调用,检查用户是否已从给定的时事通讯列表(及其变体)中订阅。
头部
主体
{ // required "user_id": 1, // Integer; ID of user "email": "test@test.sk", // String; Email of user, "list_id": 1 // Integer; ID of newsletter "variant_id": 1 // Integer; ID of variant }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/is-subscribed \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "user_id": 123, "email": "test@test.sk", "list_id": 1, "variant_id": 99, }'
响应
{ "is_subscribed": true }
POST /api/v1/users/user-preferences
API 调用以获取已订阅的时事通讯列表及其变体。
头部
主体
{ //required "user_id": 1, // Integer; ID of user "email": "test@test.com", // String; Email to get preferences for // optional "subscribed": true // Boolean; Get only subscribed newsletters }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/user-preferences \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "user_id": 123, "email": "test@test.com" }'
响应
[ { "id": 1, "code": "demo-weekly-newsletter", "title": "DEMO Weekly newsletter", "is_subscribed": true, "variants": [], "updated_at": "2020-04-06T00:15:32+02:00" }, { "id": 2, "code": "123", "title": "Test", "is_subscribed": true, "variants": [ { "id": 2, "code": "test-variant", "title": "Test Variant" } ], "updated_at": "2020-04-10T15:27:35+02:00" } ]
POST /api/v1/users/subscribe
API 调用将电子邮件地址订阅到给定的时事通讯。时事通讯必须已经创建。目前没有针对该操作的 API 端点,并且需要手动创建时事通讯。请访问 /list/new
通过网页管理员创建时事通讯。
如果时事通讯列表具有变体,则以下行为适用
- 对于
is_multi_variant=true
的时事通讯 没有 默认变体的,当用户订阅时事通讯时,将订阅所有变体。 - 对于
is_multi_variant=true
的时事通讯 有 默认变体的,当用户订阅时事通讯时,仅订阅默认变体。 - 对于
is_multi_variant=false
的时事通讯 没有 默认变体的,当用户订阅时事通讯时,不会自动订阅任何变体。在这种情况下,应明确提供变体。 - 对于
is_multi_Variant=false
的时事通讯 有 默认变体的,除非明确提供变体,否则将自动订阅默认变体。
请注意,可以通过发送 force_no_variant_subscription=true
来抑制描述的变体订阅行为。如果发送,则不会订阅任何变体,即使已明确提供。
头部
主体
{ "email": "admin@example.com", // String; email of the user "user_id": 123, // Integer; ID of the user // one of the following is required "list_id": 14, // Integer; ID of the newsletter list you're subscribing the user to "list_code": "alerts", // String; code of the newsletter list you're subscribing the user to // optional "variant_id": 123, // Integer; ID of the newsletter variant to subscribe "variant_code": "author.123", // String; Code of the newsletter variant to subscribe "send_accompanying_emails": true, // Boolean; Whether to send welcome or goodbye email to the subscribed/unsubscribed user. Defaults to TRUE. "force_no_variant_subscription": true, // Boolean; If list_id has variants, flag indicates whether the default behavior should be suppressed and no variant subscribed. Defaults to FALSE. // optional RTM parameters for tracking "what" made the user unsubscribe "rtm_params": { // Object; optional RTM parameters for pairing which email caused the user to unsubscribe. RTM params are generated into the email links automatically. "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" } }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/subscribe \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "email": "admin@example.com", "user_id": 123, "list_id": 1, "variant_id": 1, "send_accompanying_emails": true, // optional RTM parameters for tracking "what" made the user subscribe "rtm_params": { "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" }, }'
响应
{ "status": "ok", "subscribed_variants": [ // in case mail_type has variants, list of subscribed variants is listed here {id: 2, code: "test", title: "test", sorting: 1}, {id: 3, code: "test2", title: "test 2", sorting: 2} ] }
POST /api/v1/users/un-subscribe
API调用将电子邮件地址从指定的新闻通讯中取消订阅。可以选择指定要取消订阅的通讯版本。默认情况下,如果列表类型有多个版本且用户取消订阅所有版本,系统也会从主通讯列表中取消订阅。要更改默认行为,可以指定keep_list_subscription
参数 - 如果true
,则此端点调用即使在没有任何版本订阅的情况下也会保留主列表订阅。
端点接受可选的RTM(REMP的UTM)参数数组。通过Mailer发送的每封电子邮件中的每个链接都包含引用特定发送电子邮件实例的RTM参数。如果用户通过特定电子邮件取消订阅,您的前端也会接收到特殊的RTM参数,这些参数可以传递给此API调用。例如,取消订阅我们每日新闻通讯的链接可能看起来像这样
https://predplatne.dennikn.sk/mail/mail-settings/un-subscribe-email/newsletter_daily?rtm_source=newsletter_daily&rtm_medium=email&rtm_campaign=daily-newsletter-11.3.2019-personalized&rtm_content=26026
newsletter_daily
代表新闻通讯列表代码。RTM参数引用生成此电子邮件的特定电子邮件和特定批次。如果您不提供/传递这些RTM参数,则无法获得有关电子邮件取消订阅率的统计信息。
头部
主体
{ "email": "admin@example.com", // String; email of the user "user_id": 123, // Integer; ID of the user // one of the following is required "list_id": 14, // Integer; ID of the newsletter list you're subscribing the user to "list_code": "alerts", // String; code of the newsletter list you're subscribing the user to // optional "variant_id": 1, // Integer; ID of newsletter variant to unsubscribe "variant_code": "author.123", // String; Code of the newsletter variant to subscribe // optional, useful only when variant_id or variant_code is specified "keep_list_subscription": false, // Boolean; specifies if list subscription should be kept when no list variant remains subscribed // optional RTM parameters for tracking "what" made the user unsubscribe "rtm_params": { // Object; optional RTM parameters for pairing which email caused the user to unsubscribe. RTM params are generated into the email links automatically. "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" } }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/un-subscribe \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "email": "admin@example.com", "user_id": 12, "list_id": 1, "variant_id": 1, "rtm_params": { "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" } }'
响应
{ "status": "ok" }
POST /api/v1/users/bulk-subscribe
批量订阅允许批量订阅和取消订阅多个用户。有关订阅/取消订阅的详细信息,请参阅上面的单独调用。
头部
主体
{ "users": [ { "email": "admin@example.com", // String; email of the user "user_id": 12345, // Integer; ID of the user // one of the following is required "list_id": 14, // Integer; ID of the newsletter list you're subscribing the user to "list_code": "alerts", // String; code of the newsletter list you're subscribing the user to "variant_id": 3, // Integer; ID of the variant of newsletter list you're subscribing user to. Must belong to provided list. "variant_code": "author.123", // String; Code of the newsletter variant to subscribe "subscribe": false, // Boolean; indicates if you want to subscribe or unsubscribe user // optional RTM parameters used only if `subscribe:false` for tracking "what" made the user unsubscribe "rtm_params": { // Object; optional RTM parameters for pairing which email caused the user to unsubscribe. RTM params are generated into the email links automatically. "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" }, // optional "send_accompanying_emails": true, // Boolean; Flag whether to send welcome or goodbye email to the user whom subscription is being changed. Defaults to TRUE. "force_no_variant_subscription": true // Boolean; If list_id has variants, flag indicates whether the default behavior should be suppressed and no variant subscribed. Defaults to FALSE. } //... ] }
一个用户元素的属性
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/bulk-subscribe \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "users": [ { "email": "admin@example.com", "user_id": 1, "subscribe": true, "list_id": 2 }, { "email": "test@example.com", "user_id": 2, "subscribe": true, "list_code": "demo-weekly-newsletter", "variant_id": 4 }, { "email": "silent@example.com", "user_id": 3, "subscribe": false, "list_id": 3, "rtm_params": { "rtm_source": "newsletter_daily", "rtm_medium": "email", "rtm_campaign": "daily-newsletter-11.3.2019-personalized", "rtm_content": "26026" } } ] }'
响应
{ "status": "ok" }
错误示例
curl -X POST \ http://mailer.remp.press/api/v1/users/bulk-subscribe \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "users": [ { "email": "admin@example.com" }, { "user_id": "67890" }, { "email": "qa@example.com", "user_id": "qa123" }, { "email": "qa1@example.com", "user_id": "123" }, { "email": "qa2@example.com", "user_id": "123", "list_id": 1 } ] }'
错误响应
{ "status": "error", "message": "Input data contains errors. See included list of errors.", "errors": { "element_0": "Required field missing: `user_id`.", "element_1": "Required field missing: `email`.", "element_2": "Parameter `user_id` must be integer. Got [qa123].", "element_3": "Required field missing: `list_id` or `list_code`.", "element_4": "Required field missing: `subscribe`.", } }
GET /api/v1/users/check-token
验证电子邮件中提供的自动登录令牌的有效性。
每封电子邮件可以在模板中包含{{ autologin }}
块。当用于URL(例如https://predplatne.dennikn.sk{{ autologin }}
)时,会生成并附加到URL的特殊令牌。
令牌作为查询参数token
附加,例如
https://predplatne.dennikn.sk/?token=206765522b71289504ae766389bf741x
您的前端应用程序可以在访问时读取此令牌,并通过此API验证它是否仍然有效。如果有效,您可以根据端点提供的ID/电子邮件自动登录用户。
主体
{ "token": "206765522b71289504ae766389bf741x", // String; token read from query string }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/check-token \ -H 'Content-Type: application/json' \ -d '{ "token": "206765522b71289504ae766389bf741x" }'
响应
{ "status": "ok", "email": "admin@example.com" }
POST /api/v1/users/email-changed
如果您的系统允许用户更改他们的电子邮件地址,Mailer需要通知地址更改,因为订阅信息是存储在user_id/email级别的。
头部
参数
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/email-changed \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'original_email=admin%40example.com&new_email=user%40example.com'
响应
{ "status": "ok" }
POST /api/v1/users/logs-count-per-status
根据给定的时间范围返回符合状态条件的电子邮件数量。对于每个选定的状态分别返回计数。
头部
主体
{ // required "email": "test@test.com", // String; email "filter": ["sent_at", "delivered_at"], // optional "from": "2020-04-07T13:33:44+02:00", // String - RFC 3339 format; Restrict results to specific from date, optional "to": "2020-04-10T13:33:44+02:00" // String - RFC 3339 format; Restrict results to specific to date, optional }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/logs-count-per-status \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "email": "test@test.com", // String; email "filter": ["sent_at", "delivered_at"], "from": "2020-04-07T13:33:44+02:00", // String - RFC 3339 format; Restrict results to specific from date, optional "to": "2020-04-10T13:33:44+02:00" // String - RFC 3339 format; Restrict results to specific to date, optional }'
响应
{ "sent_at": 2, "delivered_at": 2 }
POST /api/v1/users/logs
根据给定标准返回邮件日志。
头部
主体
{ // required only one of email/user_id "email": "test@test.com", // String; email "user_id": 123, // Integer // optional "filter": { // Available filters are delivered_at, clicked_at, opened_at, dropped_at, spam_complained_at, hard_bounced_at "hard_bounced_at": { "from": "2020-04-07T13:33:44+02:00", // String - RFC 3339 format; Restrict results to specific from date, optional "to": "2020-04-10T13:33:44+02:00" // String - RFC 3339 format; Restrict results to specific to date, optional } }, "mail_template_ids": [1,2,3], // Array of integers; Ids of templates "page": 1, // Integer; "limit": 2 // Integer; Limit of results per page }
过滤器也可以是以下格式
{ "filter": ["dropped_at", "delivered_at"] // Available filters are sent_at, delivered_at, clicked_at, opened_at, dropped_at, spam_complained_at, hard_bounced_at }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/logs \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "filter": { "dropped_at": {}, "delivered_at": { "to": "2020-04-07T13:33:44+02:00" }, "spam_complained_at": { "from": "2020-04-07T13:33:44+02:00" }, "hard_bounced_at": { "from": "2020-04-07T13:33:44+02:00", "to": "2020-04-10T13:33:44+02:00" } }, "email": "test@test.com", "user_id": 123, "mail_template_ids": [1,2,3], "page": 1, "limit": 2 }'
响应
[ { "id": 2, "email": "test@test.com", "subject": null, "mail_template": { "id": 1, "code": "demo_email", "name": "Demo email" }, "sent_at": "2020-04-08T19:26:00+02:00", "delivered_at": "2020-04-08T13:33:44+02:00", "dropped_at": "2020-04-08T19:28:36+02:00", "spam_complained_at": null, "hard_bounced_at": null, "clicked_at": null, "opened_at": null, "attachment_size": null }, { "id": 4, "email": "test@test.com", "subject": null, "mail_template": { "id": 2, "code": "example_email", "name": "Example email" }, "sent_at": "2020-04-08T19:26:00+02:00", "delivered_at": null, "dropped_at": "2020-04-08T19:28:46+02:00", "spam_complained_at": null, "hard_bounced_at": null, "clicked_at": null, "opened_at": null, "attachment_size": null } ]
POST /api/v1/users/delete
删除给定电子邮件的所有用户数据
头部
主体
{ "email": "example@example.com" // String; email }
示例
curl -X POST \ http://mailer.remp.press/api/v1/users/delete \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "email": "example@example.com" }'
响应
- 如果删除了用户数据,将返回HTTP代码204 No Content。
- 如果没有找到记录,将返回HTTP代码404 Not found。
{ "status":"error", "code":"user_not_found", "message":"No user data found for email [example@example.com]." }
GET /api/v1/mailers/mail-types
列出所有可用的新闻通讯列表(邮件类型)。当通过API创建新的电子邮件模板时,需要提供新闻通讯的代码。
头部
参数
示例
curl -X GET \ http://mailer.remp.press/api/v1/mailers/mail-types?public_listing=1&code=demo-weekly-newsletter \ -H 'Authorization: Bearer XXX'
响应
{ "status": "ok", "data": [ { "id": 1, "code": "demo-weekly-newsletter", "image_url": null, "preview_url": null, "page_url": null, "title": "DEMO Weekly newsletter", "description": "Example mail list", "locked": false, "is_multi_variant": true, "variants": { "2": "test", "3": "test2" } } ] }
GET /api/v2/mailers/mail-types
列出所有可用的新闻通讯列表(邮件类型)。当通过API创建新的电子邮件模板时,需要提供新闻通讯的代码。
头部
参数
示例
curl -X GET \ http://mailer.remp.press/api/v1/mailers/mail-types?public_listing=1&code[]=demo-weekly-newsletter \ -H 'Authorization: Bearer XXX'
响应
{ "status": "ok", "data": [ { "id": 1, "code": "demo-weekly-newsletter", "image_url": null, "preview_url": null, "page_url": null, "title": "DEMO Weekly newsletter", "description": "Example mail list", "locked": false, "is_multi_variant": true, "variants": { "2": "test", "3": "test2" } } ] }
GET /api/v3/mailers/mail-types
列出所有可用的新闻通讯列表(邮件类型)。当通过API创建新的电子邮件模板时,需要提供新闻通讯的代码。与v2相比,v3返回所有重要的版本属性(id
、code
、sorting
、title
)。
头部
参数
示例
curl -X GET \ http://mailer.remp.press/api/v1/mailers/mail-types?public_listing=1&code[]=demo-weekly-newsletter \ -H 'Authorization: Bearer XXX'
响应
{ "status": "ok", "data": [ { "id": 1, "code": "demo-weekly-newsletter", "image_url": null, "preview_url": null, "page_url": null, "title": "DEMO Weekly newsletter", "description": "Example mail list", "locked": false, "is_multi_variant": true, "variants": { "2": {id: 2, code: "test", title: "test", sorting: 1}, "3": {id: 3, code: "test2", title: "test 2", sorting: 2} } } ] }
GET /api/v1/mailers/mail-type-categories
获取新闻通讯的可选分类。
头部
示例
curl -X GET \
http://mailer.remp.press/api/v1/mailers/mail-type-categories \
-H 'Authorization: Bearer XXX'
响应
[ { "id": 1, "title": "Newsletters", "sorting": 100, "show_title": true, "code": "newsletters" }, { "id": 2, "title": "System", "sorting": 999, "show_title": false, "code": "system" } ]
POST /api/v1/mailers/mail-type-upsert
创建或更新邮件类型(新闻通讯列表)。端点补充了通过Web界面创建新闻通讯列表的过程。
如果提供了现有的id
/code
,API处理程序将更新现有记录,否则将创建记录。字段id
在查找现有记录时具有更高的优先级。
头部
主体
{ "mail_type_category_id": 5, // Integer, required; Reference to mail type category. "priority": 100, // Integer, required; Priority of newsletter during sending. Higher number is prioritized in queue. "code": "premium_newsletter", // String, required; URL-friendly slug identifying mail type "title": "Foo Bar", // String, required: Title of mail type "description": "Newsletter sent to our premium subscribers", // String, required: Description of list visible in Mailer admin "mail_from": "email@example.com", // String, optional; Who should be used as a sender of email type. "sorting": 100, // Integer, optional; Indicator of how the mail types should be sorted in API and web. Sorting is in ascending order. "locked": false, // Boolean, optional; Flag indicating whether users should be able to subscribe/unsubscribe from the list (e.g. you want your system emails locked and subscribed for everyone) "auto_subscribe": false, // Boolean, optional; Flag indicating whether users should be subscribed to this list automatically "public_listing": false, // Boolean, optional; Flag whether the user should see the newsletter. Defaults to false. "image_url": "http://example.com/image.jpg", // String, optional; URL of image for frontend UI. "preview_url": "http://example.com/demo.html", // String, optional; URL of example newsletter to preview content to users. "page_url": "http://example.com/page.html", // String, optional; URL of newsletter title page with description and editions. "subscribe_mail_template_code": "generic_newsletter_welcome", // String, optional; Reference to mail template that should be sent to the user as welcome email right after the subscription to the newsletter. Only system emails are supported. "unsubscribe_mail_template_code": "generic_newsletter_goodbye", // String, optional; Reference to mail template that should be sent to the user as goodbye email right after the unsubscribing from the newsletter. Only system emails are supported. }
示例
curl -X POST \ http://mailer.remp.press/api/v1/mailers/mail-type-upsert \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -b PHPSESSID=cfa9527535e31a0ccb678f792299b0d2 \ -d '{ "mail_type_category_id": 5, "priority": 100, "code": "foo-bar", "title": "Foo Bar", "description": "Testing list" }'
响应
{ "status": "ok", "data": { "id": 23, "code": "foo-bar", "title": "Foo Bar", "sorting": 15, "description": null, "mail_from": null, "priority": 100, "mail_type_category_id": 5, "locked": false, "public_listing": true, "auto_subscribe": false, "image_url": null, "preview_url": null, "page_url": null, "created_at": "2019-06-27T14:08:25+02:00", "updated_at": "2019-06-27T14:08:36+02:00", "is_multi_variant": false, "default_variant_id": null, "subscribe_mail_template_code": null, "unsubscribe_mail_template_code": null } }
GET /api/v1/mailers/mail-templates
获取可用的邮件模板。可以通过mail_type_code
进行筛选,以仅获取属于指定新闻通讯列表的电子邮件。
头部
示例
curl -X GET \
http://mailer.remp.press/api/v1/mailers/mail-templates \
-H 'Authorization: Bearer XXX'
响应
[ { "code": "email_1", "name": "Welcome email", "description": "Email sent after new registration", "mail_type_code": "system" }, { "code": "email_2", "name": "Reset password", "description": "Email sent after new password was requested", "mail_type_code": "system" } ]
GET /api/v1/mailers/templates
获取可用的电子邮件模板列表。
头部
参数
示例
curl --location --request GET \ 'mailer.remp.press/api/v1/mailers/templates?mail_type_codes[]=onboarding&mail_type_codes[]=system&with_mail_types=true&page=1&limit=5' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer XXX'
响应
[ { "code": "reset_password", "name": "Password reset", "mail_type_code": "system", "attachments_enabled": true, "mail_type": { "code": "system", "title": "System emails", "description": "important notifications", "sorting": 1 } } ]
POST /api/v1/mailers/templates
创建新的电子邮件模板。端点补充了通过Web界面创建模板的过程。
头部
参数
示例
curl -X POST \ http://mailer.remp.press/api/v1/mailers/templates \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'name=Breaking%20News%20-%20Trump%20elected%20president&code=20161108_breaking_news_trump_elected_president&description=Generated%20by%20CLI%20script&mail_layout_id=1&mail_type_code=alerts&from=info%40dennikn.sk&subject=Breaking%20News%20-%20Trump%20elected%20president&template_text=This%20is%20a%20demo%20text%20of%20email&template_html=%3Cp%3EThis%20is%20a%20%3Cstrong%3Edemo%3C%2Fstrong%3E%20text%20of%20email%3C%2Fp%3E'
响应
{ "status": "ok", "id": 24832, // Integer; ID of created email "code": "20161108_breaking_news_trump_elected_president" // String; code of created email }
GET /api/v1/mailers/generator-templates
端点根据所选的生成器模板和基于使用的生成器提供的任意参数,生成电子邮件的HTML和文本内容。它补充了通过Web界面生成HTML/文本内容。
头部
示例
curl -X GET \ http://mailer.remp.press/api/v1/mailers/generator-templates \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded'
响应
{ "status": "ok", "data": [ // Array; list of available generator templates { "id": 6, // Integer; ID of generator template "title": "Breaking news - alert" // String; user-friendly name of the generator template } // ... ] }
POST /api/v1/mailers/generate-mail
端点根据所选的生成器模板和基于使用的生成器提供的任意参数,生成电子邮件的HTML和文本内容。它补充了通过Web界面生成HTML/文本内容。
头部
参数
其他参数因生成器而异,需要了解生成器的实现。请参阅生成器的apiParams()
方法以获取可用/必需参数列表。
示例
该命令使用与UrlParserGenerator链接的生成器模板。
curl -X POST \ http://mailer.remp.press/api/v1/mailers/generate-mail \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'source_template_id=17&articles=https%3A%2F%2Fdennikn.sk%2F1405858%2Fkedysi-bojovala-za-mier-v-severnom-irsku-teraz-chce-zastavit-brexit%2F%3Fref%3Dtit%0Ahttps%3A%2F%2Fdennikn.sk%2F1406263%2Fpodpora-caputovej-je-tazky-hriech-tvrdil-arcibiskup-predstavitelia-cirkvi-odsudili-aj-radicovu-pred-desiatimi-rokmi%2F%3Fref%3Dtit&footer=%20&intro=%20&rtm_campaign=%20'
响应
{ "status": "ok", "data": { "htmlContent": " -- generated HTML content of email", // String; generated HTML email "textContent": " -- generated text content of email " // String; generated plain text email } }
GET /api/v1/mailers/render-template
通过代码获取渲染的电子邮件内容。提供HTML和文本两种格式。
头部
参数
示例
curl --location --request GET \ 'mailer.remp.press/api/v1/mailers/render-template?code=demo-email' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer XXX'
响应
{ "status": "ok", "html": "<Rendered HTML of selected template>", "text": "<Rendered Text of selected template>", }
GET /api/v1/segments/list
列出所有可用的段,这些段可以用在作业中。
头部
示例
curl -X GET \
http://mailer.remp.press/api/v1/segments/list \
-H 'Authorization: Bearer XXX'
响应
{ "status": "ok", "data": [ // Array; list of available segments { "name": "Všetci používatelia", // String; User-friendly label of segment "code": "all_users", // String; Machine-friendly code of segment (slug) "provider": "crm-segment" // String; Provider (owning party) of segment } // ... ] }
POST /api/v1/mailers/jobs
创建一个新的发送作业,配置单个批量立即发送到随机电子邮件列表。作业自动处于就绪状态,指示处理守护进程立即发送。
端点补充了通过Web界面手动创建作业。
头部
参数
示例
curl -X POST \ http://mailer.remp.press/api/v1/mailers/jobs \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'template_id=24832&segment_code=users_with_print_in_past&segment_provider=crm-segment&context=123&mail_type_variant_code=variant-1&start_at=2023-12-29T10:05:00Z'
响应
{ "status": "ok", "id": 24066 // Integer; ID of created job }
POST /api/v1/mailers/preprocess-generator-parameters
解析任意输入(通常是第三方(例如,WordPress)提供的数据)并返回可用于提交给生成器(通过API或Web)的生成器参数。
请参阅实现生成器部分
中的preprocessParameters()
部分以获取集成示例。
头部
主体
{ "source_template_id": 17, // Integer; referencing generator template ID "data": { // Object; arbitrary data for generator to evaluate and process // ... } }
示例
以下示例使用NewsfilterGenerator
,它期望从输入的WordPress文章中获取值。预处理预期JSON编码的WordPress文章实例。我们只包括必要的参数以展示生成器所做的转换。
curl -X POST \ http://mailer.remp.press/api/v1/mailers/preprocess-generator-parameters \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "source_template_id": 20, "data": { "post_title": "Example article", "post_url": "https://www.example.com/article", "post_excerpt": "Lorem ipsum...", "post_content": " -- Wordpress text content --", "post_authors": [ { "display_name": "Admin Admin" } ] } }'
响应
{ "status": "ok", "data": { "editor": "Admin Admin", "title": "Example article", "url": "https://www.example.com/article", "summary": "Lorem ipsum...", "newsfilter_html": " -- Wordpress text content --", "source_template_id": 20 }, "generator_post_url": "http://mailer.remp.press/mail-generator/" }
转换后的data
可以然后用作/api/v1/mailers/generate-mail
端点的参数。
截至本描述编写时,并非所有生成器都需要提供参数的预处理。这是调用者的责任,了解源模板是否使用支持预处理的生成器。如果对不支持预处理的生成器调用preprocess,则返回带有错误信息的HTTP 400 Bad Request。
POST /api/v1/mailers/mailgun
用于旧版Mailgun事件报告的Webhook端点。我们建议使用此端点的v2
版本和新版的Mailgun Webhook实现。
您可以在Mailgun的控制面板中配置Mailgun webhook。有关Mailgun webhook的更多信息,请参阅文档、快速指南或Webhook指南。
Webhook配置足以在Mailer中启用跟踪。以下示例主要出于测试目的和完整性展示。
参数
示例
此示例仅用于调试目的,您实际上不需要自己调用它。
curl -X POST \ http://mailer.remp.press/api/v1/mailers/mailgun \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'mail_sender_id=foo×tamp=1529006854&token=a8ce0edb2dd8301dee6c2405235584e45aa91d1e9f979f3de0&signature=d2271d12299f6592d9d44cd9d250f0704e4674c30d79d07c47a66f95ce71cf55&recipient=admin%40example.com&event=opened'
响应
{ "status": "ok", }
事件本身仅经过验证,并放入异步队列以稍后处理。
POST /api/v2/mailers/mailgun
用于新Mailgun事件报告的Webhook端点。与v1
相比,Mailgun提供的有效负载更详细,并以JSON主体形式发送,而不是HTTP表单参数。
您可以在Mailgun的控制面板中配置Mailgun webhook。有关Mailgun webhook的更多信息,请参阅文档、快速指南或Webhook指南。
Webhook配置足以在Mailer中启用跟踪。以下示例主要出于测试目的和完整性展示。
主体
{ "signature": { "timestamp": "1529006854", "token": "a8ce0edb2dd8301dee6c2405235584e45aa91d1e9f979f3de0", "signature": "d2271d12299f6592d9d44cd9d250f0704e4674c30d79d07c47a66f95ce71cf55" }, "event-data": { "event": "opened", "timestamp": 1529006854.329574, "id": "DACSsAdVSeGpLid7TN03WA", // ... } }
示例
此示例仅用于调试目的,您实际上不需要自己调用它。更详细的示例可以在Mailgun的博客文章中找到,该文章介绍了Webhook的新版本。
curl -X POST \ http://mailer.remp.press/api/v2/mailers/mailgun \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "signature": { "timestamp": "1529006854", "token": "a8ce0edb2dd8301dee6c2405235584e45aa91d1e9f979f3de0", "signature": "d2271d12299f6592d9d44cd9d250f0704e4674c30d79d07c47a66f95ce71cf55" }, "event-data": { "event": "opened", "timestamp": 1529006854.329574, "id": "DACSsAdVSeGpLid7TN03WA" } }'
响应
{ "status": "ok", }
事件本身仅经过验证,并放入异步队列以稍后处理。
POST /api/v1/mailers/send-email
用于发送单个邮件而不创建任务的端点。它主要应用于系统(基于事件的)邮件和测试邮件。
主体
{ "mail_template_code": "welcome_email_with_password", "email": "admin@example.com", "params": { // optional: key-value string pairs of parameters to be used mail_template variables "email": "admin@example.com", "password": "secret" }, "context": "user.welcome.123", // optional: if email with the same context and mail_template_code was already sent, Mailer will not send the email again "attachments": [ // optional { "file": "/path/to/file", // used to determine name of attachment and possibly content of attachment "content": "-- base64 encoded content of attachment --" // if content is not provided, Mailer attempts to open file based on provided path in "file" property } ], "schedule_at": "2019-09-23T08:50:03+00:00", // optional: RFC3339-formatted date when email should be sent; if not provided, email is scheduled to be sent immediately "locale": "en" // optional: specify language version of email }
示例
curl -X POST \ http://mailer.remp.press/api/v1/mailers/send-email \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "mail_template_code": "welcome_email_with_password", "email": "admin@example.com" }
响应
{ "status": "ok", "message": "Email was scheduled to be sent." }
实际的发送由后台工作进程异步处理,可能会根据系统资源和后台处理队列的大小而延迟。
POST /api/v1/mailers/mail-type-variants
创建新的邮件类型变体。
主体
{ "mail_type_code": "type-25", "title": "Variant 1", "code": "variant-1", "sorting": 100 }
示例
curl -X POST \ http://mailer.remp.press/api/v1/mailers/mail-type-variants \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "mail_type_code": "type-25", "title": "Variant 1", "code": "variant-1", "sorting": 100 }
响应
{ "status": "ok", "id": 24066 // Integer; ID of created mail type variant }