Add e-mail webhook.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-03-06 03:27:55 -06:00
parent 92fbac4006
commit ace826dae5
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
6 changed files with 231 additions and 14 deletions

View File

@ -5,10 +5,13 @@ release channel, you can take advantage of these new features and fixes.
## New Features/Changes ## New Features/Changes
- **Self-Service Password Reset**: If SMTP is configured properly in the system settings page, users can now request a - **E-mail Delivery**: System administrators can now configure SMTP for e-mail delivery via the system settings page. If
password reset directly from the login page instead of requiring administrator intervention. The password reset SMTP is enabled for your installation, the following functionality is added:
functionality creates a single-time-use login token that can be used to reset the user's password and log them in
once. - **Self-Service Password Reset**: Users can request a password recovery token to reset their own passwords.
- **E-mail Web Hook**: You can dispatch an e-mail to specified recipients as a web hook when specific triggers
occur.
- Web Hooks can now be triggered to dispatch when a station goes offline or comes online. - Web Hooks can now be triggered to dispatch when a station goes offline or comes online.

View File

@ -0,0 +1,96 @@
<?php
/**
* @var array $triggers
* @var App\Environment $environment
* @var App\Http\Router $router
*/
return [
'method' => 'post',
'groups' => [
'message_grp' => [
'use_grid' => true,
'elements' => [
'name' => [
'text',
[
'label' => __('Web Hook Name'),
'description' => __(
'Choose a name for this webhook that will help you distinguish it from others. This will only be shown on the administration page.'
),
'required' => true,
'form_group_class' => 'col-md-6',
],
],
'triggers' => [
'multiCheckbox',
[
'label' => __('Web Hook Triggers'),
'options' => $triggers,
'required' => true,
'form_group_class' => 'col-sm-12',
],
],
'to' => [
'text',
[
'label' => __('Message Recipient(s)'),
'belongsTo' => 'config',
'required' => true,
'description' => __('E-mail addresses can be separated by commas.'),
'form_group_class' => 'col-sm-6',
],
],
'subject' => [
'text',
[
'label' => __('Message Subject'),
'belongsTo' => 'config',
'required' => true,
'description' => __(
'Variables are in the form of <code>{{ var.name }}</code>. All values in the <a href="%s" target="_blank">Now Playing API response</a> are avaliable for use. Any empty fields are ignored.',
$router->named('api:nowplaying:index')
),
'form_group_class' => 'col-sm-6',
],
],
'message' => [
'textarea',
[
'label' => __('Message Body'),
'belongsTo' => 'config',
'required' => true,
'description' => __(
'Variables are in the form of <code>{{ var.name }}</code>. All values in the <a href="%s" target="_blank">Now Playing API response</a> are avaliable for use. Any empty fields are ignored.',
$router->named('api:nowplaying:index')
),
'form_group_class' => 'col-sm-12',
],
],
],
],
'submit_grp' => [
'elements' => [
'submit' => [
'submit',
[
'type' => 'submit',
'label' => __('Save Changes'),
'class' => 'ui-button btn-lg btn-primary',
],
],
],
],
],
];

View File

@ -13,6 +13,11 @@ return [
'name' => __('Generic Web Hook'), 'name' => __('Generic Web Hook'),
'description' => __('Automatically send a message to any URL when your station data changes.'), 'description' => __('Automatically send a message to any URL when your station data changes.'),
], ],
Connector\Email::NAME => [
'class' => Connector\Email::class,
'name' => __('Send E-mail'),
'description' => __('Send an e-mail to specified address(es).'),
],
Connector\TuneIn::NAME => [ Connector\TuneIn::NAME => [
'class' => Connector\TuneIn::class, 'class' => Connector\TuneIn::class,
'name' => __('TuneIn AIR'), 'name' => __('TuneIn AIR'),

View File

@ -7,28 +7,24 @@ use App\Exception\RateLimitExceededException;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\RateLimit; use App\RateLimit;
use App\Service\Mail;
use App\Session\Flash; use App\Session\Flash;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
class ForgotPasswordAction class ForgotPasswordAction
{ {
public function __invoke( public function __invoke(
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
Entity\Repository\SettingsRepository $settingsRepo,
Entity\Repository\UserRepository $userRepo, Entity\Repository\UserRepository $userRepo,
Entity\Repository\UserLoginTokenRepository $loginTokenRepo, Entity\Repository\UserLoginTokenRepository $loginTokenRepo,
RateLimit $rateLimit, RateLimit $rateLimit,
MailerInterface $mailer Mail $mail
): ResponseInterface { ): ResponseInterface {
$flash = $request->getFlash(); $flash = $request->getFlash();
$view = $request->getView(); $view = $request->getView();
$settings = $settingsRepo->readSettings(); if (!$mail->isEnabled()) {
if (!$settings->getMailEnabled()) {
return $view->renderToResponse($response, 'frontend/account/forgot_disabled'); return $view->renderToResponse($response, 'frontend/account/forgot_disabled');
} }
@ -55,8 +51,7 @@ class ForgotPasswordAction
$user = $userRepo->findByEmail($email); $user = $userRepo->findByEmail($email);
if ($user instanceof Entity\User) { if ($user instanceof Entity\User) {
$email = new Email(); $email = $mail->createMessage();
$email->from(new Address($settings->getMailSenderEmail(), $settings->getMailSenderName()));
$email->to($user->getEmail()); $email->to($user->getEmail());
$email->subject(__('Account Recovery Link')); $email->subject(__('Account Recovery Link'));
@ -71,7 +66,7 @@ class ForgotPasswordAction
) )
); );
$mailer->send($email); $mail->send($email);
} }
$flash->addMessage( $flash->addMessage(

44
src/Service/Mail.php Normal file
View File

@ -0,0 +1,44 @@
<?php
namespace App\Service;
use App\Entity;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\RawMessage;
class Mail implements MailerInterface
{
protected Entity\Repository\SettingsRepository $settingsRepo;
protected MailerInterface $mailer;
public function __construct(Entity\Repository\SettingsRepository $settingsRepo, MailerInterface $mailer)
{
$this->settingsRepo = $settingsRepo;
$this->mailer = $mailer;
}
public function isEnabled(): bool
{
$settings = $this->settingsRepo->readSettings();
return $settings->getMailEnabled();
}
public function createMessage(): Email
{
$settings = $this->settingsRepo->readSettings();
$email = new Email();
$email->from(new Address($settings->getMailSenderEmail(), $settings->getMailSenderName()));
return $email;
}
public function send(RawMessage $message, Envelope $envelope = null): void
{
$this->mailer->send($message, $envelope);
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Webhook\Connector;
use App\Entity;
use App\Service\Mail;
use GuzzleHttp\Client;
use Monolog\Logger;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
class Email extends AbstractConnector
{
public const NAME = 'email';
protected Mail $mail;
public function __construct(
Logger $logger,
Client $httpClient,
Mail $mail
) {
parent::__construct($logger, $httpClient);
$this->mail = $mail;
}
public function dispatch(
Entity\Station $station,
Entity\StationWebhook $webhook,
Entity\Api\NowPlaying $np,
array $triggers,
bool $isStandalone
): bool {
if (!$this->mail->isEnabled()) {
$this->logger->error('E-mail delivery is not currently enabled. Skipping webhook delivery...');
return false;
}
$config = $webhook->getConfig();
$emailTo = $config['to'];
$emailSubject = $config['subject'];
$emailBody = $config['message'];
if (empty($emailTo) || empty($emailSubject) || empty($emailBody)) {
$this->logger->error('Webhook ' . self::NAME . ' is missing necessary configuration. Skipping...');
return false;
}
try {
$email = $this->mail->createMessage();
$emailToParts = explode(',', $emailTo);
foreach ($emailToParts as $emailToPart) {
$email->addTo(trim($emailToPart));
}
$vars = [
'subject' => $emailSubject,
'body' => $emailBody,
];
$vars = $this->replaceVariables($vars, $np);
$email->subject($vars['subject']);
$email->text($vars['body']);
$this->mail->send($email);
} catch (TransportExceptionInterface $e) {
$this->logger->error(sprintf('Error from e-mail (%d): %s', $e->getCode(), $e->getMessage()));
return false;
}
return true;
}
}