<?php
namespace App\Subscriber\Payment;
use App\Dictionary\ConversationMessageSource;
use App\Dictionary\ConversationMessageType;
use App\Dictionary\ImageFilter;
use App\Dictionary\PaymentType;
use App\Entity\MemberMedia;
use App\Entity\MemberOperator;
use App\Entity\Payment;
use App\Event\Messenger\NewMessageEvent;
use App\Event\Payment\PaymentCreatedEvent;
use App\Event\RedisEventManager;
use App\Lib\Payment\Booking;
use App\Lib\Payment\PayMethod\AdminBonusMethod;
use App\Lib\Payment\PayMethod\LiquibyteCashpayMethod;
use App\Lib\Payment\PayMethod\LiquibytePrepayMethod;
use App\Lib\Purchase\ConversationMessagePurchasable;
use App\Service\MemberService;
use App\Service\Messenger\MessengerService;
use App\Service\Payment\PurchaseService;
use Frivol\Common\Service\Notification\MessengerNotification;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ConversationMessageSubscriber implements EventSubscriberInterface
{
protected MessengerService $messenger;
protected MemberService $memberService;
protected PurchaseService $purchaseService;
protected RedisEventManager $redisEvent;
protected array $methodWhitelistForNotification = [
LiquibytePrepayMethod::NAME,
LiquibyteCashpayMethod::NAME,
AdminBonusMethod::NAME,
];
public function __construct(MemberService $memberService, MessengerService $messenger,
PurchaseService $purchaseService, RedisEventManager $redisEvent)
{
$this->memberService = $memberService;
$this->messenger = $messenger;
$this->purchaseService = $purchaseService;
$this->redisEvent = $redisEvent;
}
public static function getSubscribedEvents(): array
{
return [
PaymentCreatedEvent::class => [
['sendMessageNotification', 0],
],
NewMessageEvent::class => [
['onNewMessageHandlePurchase', 0],
['onHandwrittenMessageByAmateurCreateProvision', 1],
['sendPubSubNotification', 2],
['sendPubSubOperatorNotification', 3],
],
];
}
/**
* @throws \App\Exception\Commission\NotAnAmateurException
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function onHandwrittenMessageByAmateurCreateProvision(NewMessageEvent $event)
{
$entity = $event->getEntity();
$sender = $event->getSender();
if (!$entity->mayBeProvisionedForAmateur()) {
return;
}
$conversation = $entity->getConversation();
$openPurchases = $this->purchaseService->getOpenMessagePurchases($sender, $conversation);
foreach ($openPurchases as $purchase) {
$this->purchaseService->assignAmateurProvisionByPurchase($purchase);
}
}
/**
* @throws \App\Exception\Purchase\CoinsLockedException
* @throws \App\Exception\Purchase\InsufficientCoinsException
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function onNewMessageHandlePurchase(NewMessageEvent $event)
{
$entity = $event->getEntity();
if (!$entity->needsToBePurchased()) {
return;
}
$buyer = $event->getSender();
$seller = $entity->getConversation()->getPartner($buyer);
$purchasable = new ConversationMessagePurchasable($entity, $seller);
$this->purchaseService->handlePurchase($purchasable, $buyer);
}
/**
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function sendMessageNotification(PaymentCreatedEvent $event)
{
$payment = $event->getPayment();
if (PaymentType::BOOKING !== $payment->getType()) {
return;
}
if (!in_array($payment->getMethod(), $this->methodWhitelistForNotification)) {
return;
}
$member = $payment->getAccount()->getMember();
$support = $this->memberService->getSupportMember();
/*
* No support user configured OR support _is_ the affected member
*/
if (null === $support || $support->getId() == $member->getId()) {
return;
}
/**
* @var $booking Booking
*/
$booking = $event->getPaymentConceptObject();
if (AdminBonusMethod::NAME == $payment->getMethod() && $booking && $booking->getOverrideCoins()) {
$amount = $booking->getOverrideCoins();
} else {
$amount = number_format($payment->getAmount(), 2, ',', '.');
}
$msg = sprintf($this->getMessageFormat($payment), $member->getUsername(), $amount);
$source = ConversationMessageSource::SYSTEM;
$type = ConversationMessageType::TEXT;
$this->messenger->sendDirectMessage($support, $member, $msg, $type, $source);
}
public function sendPubSubOperatorNotification(NewMessageEvent $event): void
{
$message = $event->getEntity();
$from = $event->getSender();
$recipient = $message->getConversation()->getPartner($from);
// only interested in messages from endusers -to- amateurs
if ($from->getIsAmateur() || !$recipient->getIsAmateur()) {
return;
}
// also not interested, if the amateur does not have at least one operator
if (!$recipient->getOperators()->count()) {
return;
}
foreach ($recipient->getOperators() as $operatorRelationship) {
/**
* @var $operatorRelationship MemberOperator
*/
if (!$operatorRelationship->getIsOnline()) {
// also not interested, if it appears that the operator is not
// actively working for the amateur who received the message
continue;
}
$operatorMember = $operatorRelationship->getOperator();
if (!$operatorMember->isMemberOnline()) {
// another case: appears the operating member is not online himself
continue;
}
// all good actually. notify the frontend operator app about the incoming message
$notification = new MessengerNotification();
$data = [
'fromMember' => $from->getId(),
'fromUsername' => $from->getUsername(),
'toMember' => $recipient->getId(),
'toUsername' => $recipient->getUsername(),
'partner' => [
'id' => $from->getId(),
'username' => $from->getUsername(),
],
'message' => $event->getEntity()->getMessage(),
'conversation' => $message->getConversation()->getId(),
'source' => $message->getSource(),
'created_at' => $message->getCreatedAt()->format(\DateTimeInterface::ATOM),
];
if ($from->getMainPhoto() instanceof MemberMedia) {
$data['image'] = $from->getMainPhoto()->getUrl(ImageFilter::SIZE_100x100);
}
$notification->setData($data);
// recipient is the operator! (2nd param)
// $this->redisEvent->publishToMemberWithFrom($from, $operatorMember, $notification);
}
}
public function sendPubSubNotification(NewMessageEvent $event): void
{
$from = $event->getSender();
$message = $event->getEntity();
$recipient = $message->getConversation()->getPartner($from);
$notification = new MessengerNotification();
$data = [
'fromMember' => $from->getId(),
'fromUsername' => $from->getUsername(),
'toMember' => $recipient->getId(),
'toUsername' => $recipient->getUsername(),
'partner' => [
'id' => $from->getId(),
'username' => $from->getUsername(),
],
'message' => $event->getEntity()->getMessage(),
'conversation' => $message->getConversation()->getId(),
'source' => $message->getSource(),
'created_at' => $message->getCreatedAt()->format(\DateTimeInterface::ATOM),
];
if ($from->getMainPhoto() instanceof MemberMedia) {
$data['image'] = $from->getMainPhoto()->getUrl(ImageFilter::SIZE_100x100);
}
$notification->setData($data);
$this->redisEvent->publishToMemberWithFrom($from, $recipient, $notification);
}
public function getMessageFormat(Payment $payment): string
{
if (AdminBonusMethod::NAME == $payment->getMethod()) {
return 'Hallo %s,'.
"\nwir haben dir soeben einen Bonus in Höhe von %d Coins verbucht.".
"\nViel Spaß!".
"\nFür weitere Fragen stehen wir dir gerne zur Verfügung.".
"\nLiebe Grüße vom Frivol.com Support";
}
return 'Hallo %s,'.
"\nwir haben deine Zahlung über %s EUR erhalten und soeben verbucht. Du kannst die Coins ab sofort nutzen.".
"\nViel Spaß!".
"\nFür weitere Fragen stehen wir dir gerne zur Verfügung.".
"\nLiebe Grüße vom Frivol.com Support";
}
}