fix: send the correct body when chat notification

This commit is contained in:
Maël Gangloff 2024-08-28 17:35:32 +02:00
parent 83a97b429b
commit 02c9ce7eec
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
10 changed files with 62 additions and 103 deletions

View File

@ -13,14 +13,12 @@ identifiers:
- type: url - type: url
value: >- value: >-
https://github.com/maelgangloff/domain-watcher/releases https://github.com/maelgangloff/domain-watcher/releases
description: Release of domain-watchdog description: Release of Domain Watchdog
repository-code: 'https://github.com/maelgangloff/domain-watchdog' repository-code: 'https://github.com/maelgangloff/domain-watchdog'
abstract: An app that utilizes RDAP to gather publicly accessible information about domain names, track their history, and automatically purchase them abstract: An app that uses RDAP to collect publicly available info about domains, track their history, and purchase them
keywords: keywords:
- DOMAIN - DOMAIN
- RDAP - RDAP
- WHOIS - WHOIS
license: AGPL-3.0-or-later license: AGPL-3.0-or-later
version: 0.1.0
date-released: '2024-08-08'
license-url: 'https://github.com/maelgangloff/domain-watchdog/blob/master/LICENSE' license-url: 'https://github.com/maelgangloff/domain-watchdog/blob/master/LICENSE'

View File

@ -3,7 +3,6 @@
namespace App\Controller; namespace App\Controller;
use App\Config\Provider\AbstractProvider; use App\Config\Provider\AbstractProvider;
use App\Config\WebhookScheme;
use App\Entity\Connector; use App\Entity\Connector;
use App\Entity\Domain; use App\Entity\Domain;
use App\Entity\DomainEntity; use App\Entity\DomainEntity;
@ -12,6 +11,7 @@ use App\Entity\User;
use App\Entity\WatchList; use App\Entity\WatchList;
use App\Notifier\TestChatNotification; use App\Notifier\TestChatNotification;
use App\Repository\WatchListRepository; use App\Repository\WatchListRepository;
use App\Service\ChatNotificationService;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Exception\ORMException;
@ -39,9 +39,6 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Notifier\Exception\InvalidArgumentException;
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
use Symfony\Component\Notifier\Transport\Dsn;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;
@ -55,7 +52,8 @@ class WatchListController extends AbstractController
private readonly LoggerInterface $logger, private readonly LoggerInterface $logger,
private readonly HttpClientInterface $httpClient, private readonly HttpClientInterface $httpClient,
private readonly CacheItemPoolInterface $cacheItemPool, private readonly CacheItemPoolInterface $cacheItemPool,
private readonly KernelInterface $kernel private readonly KernelInterface $kernel,
private readonly ChatNotificationService $chatNotificationService
) { ) {
} }
@ -124,7 +122,7 @@ class WatchListController extends AbstractController
} }
} }
$this->verifyWebhookDSN($watchList); $this->chatNotificationService->sendChatNotification($watchList, new TestChatNotification());
$this->verifyConnector($watchList, $watchList->getConnector()); $this->verifyConnector($watchList, $watchList->getConnector());
$this->logger->info('User {username} registers a Watchlist ({token}).', [ $this->logger->info('User {username} registers a Watchlist ({token}).', [
@ -155,47 +153,6 @@ class WatchListController extends AbstractController
return $user->getWatchLists(); return $user->getWatchLists();
} }
private function verifyWebhookDSN(WatchList $watchList): void
{
$webhookDsn = $watchList->getWebhookDsn();
if (null !== $webhookDsn && 0 !== count($webhookDsn)) {
foreach ($webhookDsn as $dsnString) {
try {
$dsn = new Dsn($dsnString);
} catch (InvalidArgumentException $exception) {
throw new BadRequestHttpException($exception->getMessage());
}
$scheme = $dsn->getScheme();
$webhookScheme = WebhookScheme::tryFrom($scheme);
if (null === $webhookScheme) {
throw new BadRequestHttpException("The DSN scheme ($scheme) is not supported");
}
$transportFactoryClass = $webhookScheme->getChatTransportFactory();
/** @var AbstractTransportFactory $transportFactory */
$transportFactory = new $transportFactoryClass();
$push = (new TestChatNotification())->asPushMessage();
$chat = (new TestChatNotification())->asChatMessage();
try {
$factory = $transportFactory->create($dsn);
if ($factory->supports($push)) {
$factory->send($push);
} elseif ($factory->supports($chat)) {
$factory->send($chat);
} else {
throw new BadRequestHttpException('Unsupported message type');
}
} catch (\Throwable $exception) {
throw new BadRequestHttpException($exception->getMessage());
}
}
}
}
/** /**
* @throws \Exception * @throws \Exception
*/ */
@ -285,7 +242,7 @@ class WatchListController extends AbstractController
} }
} }
$this->verifyWebhookDSN($watchList); $this->chatNotificationService->sendChatNotification($watchList, new TestChatNotification());
$this->verifyConnector($watchList, $watchList->getConnector()); $this->verifyConnector($watchList, $watchList->getConnector());
$this->logger->info('User {username} updates a Watchlist ({token}).', [ $this->logger->info('User {username} updates a Watchlist ({token}).', [

View File

@ -75,12 +75,11 @@ final readonly class OrderDomainHandler
$connectorProvider = new $connectorProviderClass($connector->getAuthData(), $this->client, $this->cacheItemPool, $this->kernel); $connectorProvider = new $connectorProviderClass($connector->getAuthData(), $this->client, $this->cacheItemPool, $this->kernel);
$connectorProvider->orderDomain($domain, $this->kernel->isDebug()); $connectorProvider->orderDomain($domain, $this->kernel->isDebug());
$this->statService->incrementStat('stats.domain.purchased');
$notification = (new DomainOrderNotification($this->sender, $domain, $connector)); $notification = (new DomainOrderNotification($this->sender, $domain, $connector));
$this->mailer->send($notification->asEmailMessage(new Recipient($watchList->getUser()->getEmail()))->getMessage()); $this->mailer->send($notification->asEmailMessage(new Recipient($watchList->getUser()->getEmail()))->getMessage());
$this->chatNotificationService->sendChatNotification($watchList, $notification); $this->chatNotificationService->sendChatNotification($watchList, $notification);
$this->statService->incrementStat('stats.domain.purchased');
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
$this->logger->warning('Unable to complete purchase. An error message is sent to user {username}.', [ $this->logger->warning('Unable to complete purchase. An error message is sent to user {username}.', [
'username' => $watchList->getUser()->getUserIdentifier(), 'username' => $watchList->getUser()->getUserIdentifier(),

View File

@ -8,14 +8,11 @@ use Symfony\Component\Mime\Address;
use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\EmailMessage; use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface; use Symfony\Component\Notifier\Recipient\RecipientInterface;
class DomainOrderErrorNotification extends Notification implements ChatNotificationInterface, EmailNotificationInterface, PushNotificationInterface class DomainOrderErrorNotification extends DomainWatchdogNotification
{ {
public function __construct( public function __construct(
private readonly Address $sender, private readonly Address $sender,

View File

@ -10,14 +10,11 @@ use Symfony\Component\Mime\Email;
use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\EmailMessage; use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface; use Symfony\Component\Notifier\Recipient\RecipientInterface;
class DomainOrderNotification extends Notification implements ChatNotificationInterface, EmailNotificationInterface, PushNotificationInterface class DomainOrderNotification extends DomainWatchdogNotification
{ {
public function __construct( public function __construct(
private readonly Address $sender, private readonly Address $sender,

View File

@ -8,14 +8,11 @@ use Symfony\Component\Mime\Address;
use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\EmailMessage; use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface; use Symfony\Component\Notifier\Recipient\RecipientInterface;
class DomainUpdateErrorNotification extends Notification implements ChatNotificationInterface, EmailNotificationInterface, PushNotificationInterface class DomainUpdateErrorNotification extends DomainWatchdogNotification
{ {
public function __construct( public function __construct(
private readonly Address $sender, private readonly Address $sender,

View File

@ -9,14 +9,11 @@ use Symfony\Component\Mime\Email;
use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\EmailMessage; use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface; use Symfony\Component\Notifier\Recipient\RecipientInterface;
class DomainUpdateNotification extends Notification implements ChatNotificationInterface, EmailNotificationInterface, PushNotificationInterface class DomainUpdateNotification extends DomainWatchdogNotification
{ {
public function __construct( public function __construct(
private readonly Address $sender, private readonly Address $sender,

View File

@ -0,0 +1,11 @@
<?php
namespace App\Notifier;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
abstract class DomainWatchdogNotification extends Notification implements ChatNotificationInterface, PushNotificationInterface
{
}

View File

@ -4,12 +4,10 @@ namespace App\Notifier;
use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\PushMessage; use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\PushNotificationInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface; use Symfony\Component\Notifier\Recipient\RecipientInterface;
class TestChatNotification extends Notification implements ChatNotificationInterface, PushNotificationInterface class TestChatNotification extends DomainWatchdogNotification
{ {
public function asChatMessage(?RecipientInterface $recipient = null, ?string $transport = null): ?ChatMessage public function asChatMessage(?RecipientInterface $recipient = null, ?string $transport = null): ?ChatMessage
{ {

View File

@ -4,10 +4,11 @@ namespace App\Service;
use App\Config\WebhookScheme; use App\Config\WebhookScheme;
use App\Entity\WatchList; use App\Entity\WatchList;
use App\Notifier\TestChatNotification; use App\Notifier\DomainWatchdogNotification;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface; use Symfony\Component\Notifier\Exception\InvalidArgumentException;
use Symfony\Component\Notifier\Recipient\NoRecipient;
use Symfony\Component\Notifier\Transport\AbstractTransportFactory; use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
use Symfony\Component\Notifier\Transport\Dsn; use Symfony\Component\Notifier\Transport\Dsn;
@ -18,47 +19,54 @@ readonly class ChatNotificationService
) { ) {
} }
public function sendChatNotification(WatchList $watchList, ChatNotificationInterface $notification): void public function sendChatNotification(WatchList $watchList, DomainWatchdogNotification $notification): void
{ {
$webhookDsn = $watchList->getWebhookDsn(); $webhookDsn = $watchList->getWebhookDsn();
if (null !== $webhookDsn && 0 !== count($webhookDsn)) { if (null !== $webhookDsn && 0 !== count($webhookDsn)) {
foreach ($webhookDsn as $dsnString) { foreach ($webhookDsn as $dsnString) {
$dsn = new Dsn($dsnString); try {
$dsn = new Dsn($dsnString);
} catch (InvalidArgumentException $exception) {
throw new BadRequestHttpException($exception->getMessage());
}
$scheme = $dsn->getScheme(); $scheme = $dsn->getScheme();
$webhookScheme = WebhookScheme::tryFrom($scheme); $webhookScheme = WebhookScheme::tryFrom($scheme);
if (null !== $webhookScheme) { if (null === $webhookScheme) {
$transportFactoryClass = $webhookScheme->getChatTransportFactory(); throw new BadRequestHttpException("The DSN scheme ($scheme) is not supported");
/** @var AbstractTransportFactory $transportFactory */ }
$transportFactory = new $transportFactoryClass();
$push = (new TestChatNotification())->asPushMessage(); $transportFactoryClass = $webhookScheme->getChatTransportFactory();
$chat = (new TestChatNotification())->asChatMessage(); /** @var AbstractTransportFactory $transportFactory */
$transportFactory = new $transportFactoryClass();
try { $push = $notification->asPushMessage(new NoRecipient());
$factory = $transportFactory->create($dsn); $chat = $notification->asChatMessage(new NoRecipient());
if ($factory->supports($push)) { try {
$factory->send($push); $factory = $transportFactory->create($dsn);
} elseif ($factory->supports($chat)) {
$factory->send($chat);
} else {
throw new BadRequestHttpException('Unsupported message type');
}
$this->logger->info('Chat message sent with {schema} for Watchlist {token}', if ($factory->supports($push)) {
[ $factory->send($push);
'scheme' => $webhookScheme->name, } elseif ($factory->supports($chat)) {
'token' => $watchList->getToken(), $factory->send($chat);
]); } else {
} catch (\Throwable) { throw new BadRequestHttpException('Unsupported message type');
$this->logger->error('Unable to send a chat message to {scheme} for Watchlist {token}',
[
'scheme' => $webhookScheme->name,
'token' => $watchList->getToken(),
]);
} }
$this->logger->info('Chat message sent with {schema} for Watchlist {token}',
[
'scheme' => $webhookScheme->name,
'token' => $watchList->getToken(),
]);
} catch (\Throwable $exception) {
$this->logger->error('Unable to send a chat message to {scheme} for Watchlist {token}',
[
'scheme' => $webhookScheme->name,
'token' => $watchList->getToken(),
]);
throw new BadRequestHttpException($exception->getMessage());
} }
} }
} }