<?php
namespace App\Subscriber\Messenger;
use App\Event\Messenger\ConversationMessagePreconditionCheckEvent;
use App\Event\Messenger\FilterNewMessageEvent;
use App\Exception\Messaging\MessageBlockedUntilException;
use App\Exception\Purchase\CoinsLockedException;
use App\Exception\Purchase\InsufficientCoinsException;
use App\Exception\User\MemberBlockedException;
use App\Repository\ConversationMessageRepository;
use App\Service\Messenger\SpamDetectorService;
use App\Service\Payment\PurchaseService;
use App\Service\User\BalanceMemberService;
use App\Service\User\MemberBlockedService;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MessageSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly BalanceMemberService $balanceMemberService,
private readonly PurchaseService $purchaseService,
private readonly SpamDetectorService $spamDetector,
private readonly MemberBlockedService $blockedService,
private readonly ConversationMessageRepository $messageRepository
) {
}
public static function getSubscribedEvents(): array
{
return [
ConversationMessagePreconditionCheckEvent::class => [
['checkCanMessageUser', 0],
['checkMemberBlocked', 1],
['checkSufficientCoins', 2],
],
FilterNewMessageEvent::class => [
['onNewMessageDetectSpam', 0],
],
];
}
/**
* @throws MessageBlockedUntilException
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function checkCanMessageUser(ConversationMessagePreconditionCheckEvent $event): void
{
$message = $event->getEntity();
$sender = $message->getMember();
$recipient = $message->getRecipient();
$status = $this->messageRepository->canMessageUser($sender, $recipient);
if (!$status->isSendable()) {
$error = new MessageBlockedUntilException($status->getBlockedUntil());
$event->setStopReason($error->getMessage());
throw $error;
}
}
/**
* @throws MemberBlockedException
*/
public function checkMemberBlocked(ConversationMessagePreconditionCheckEvent $event): void
{
$message = $event->getEntity();
$sender = $message->getMember();
$recipient = $message->getRecipient();
if ($block = $this->blockedService->findByOwnerAndTarget($recipient, $sender)) {
$error = new MemberBlockedException('Dieses Mitglied hat Dich blockiert.');
$event->setStopReason($error->getMessage());
throw $error;
}
}
/**
* @throws InsufficientCoinsException
* @throws NonUniqueResultException|CoinsLockedException
*/
public function checkSufficientCoins(ConversationMessagePreconditionCheckEvent $event): void
{
$message = $event->getEntity();
$sender = $message->getMember();
// let through: sender is amateur, coins set to 0, system messages, messages from admins
if (!$message->needsToBePurchased()) {
return;
}
if (!$sender->getIsAmateur() && $this->purchaseService->areCoinsLockedForMember($sender)) {
$error = new CoinsLockedException($sender->getUsername());
$event->setStopReason($error->getEnduserMessage());
throw $error;
}
// check coins
$available = $this->balanceMemberService->getCoinsForMember($sender);
if ($available < $message->getCoins()) {
$error = new InsufficientCoinsException(
$sender->getUsername(),
$message->getCoins(),
$available
);
$event->setStopReason($error->getEnduserMessage());
throw $error;
}
}
public function onNewMessageDetectSpam(FilterNewMessageEvent $event): void
{
if (!$event->mustUseSpamDetection()) {
return;
}
$message = $event->getEntity();
$text = $message->getMessage();
$hits = $this->spamDetector->parse($text);
if ($hits > 0) {
$message->setMessage($text);
}
}
}