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
- **Self-Service Password Reset**: If SMTP is configured properly in the system settings page, users can now request a
password reset directly from the login page instead of requiring administrator intervention. The password reset
functionality creates a single-time-use login token that can be used to reset the user's password and log them in
once.
- **E-mail Delivery**: System administrators can now configure SMTP for e-mail delivery via the system settings page. If
SMTP is enabled for your installation, the following functionality is added:
- **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.

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'),
'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 => [
'class' => Connector\TuneIn::class,
'name' => __('TuneIn AIR'),

View File

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