<?php
declare(strict_types=1);
namespace App\EventsSubscriber\Analytics;
use App\Event\Activity\AcceptedEvent;
use App\Event\Activity\ActivityActivatedEvent;
use App\Event\Activity\ActivityBoostCodeReceived;
use App\Event\Activity\ActivityBoostCodeRequested;
use App\Event\Activity\ActivityDeclinedEvent;
use App\Event\Activity\CreatorCampaignAppliedEvent;
use App\Event\Activity\CreatorVideoSubmittedEvent;
use App\Event\Activity\ApproveAllReviewEvent;
use App\Event\Activity\DeclineAllReviewEvent;
use App\Event\Activity\PersonalBonusAddedEvent;
use App\Event\Activity\ReviewEvent;
use App\Event\Campaign\ActivatedEvent;
use App\Event\Campaign\CampaignBudgetIncreasedEvent;
use App\Event\Campaign\CampaignCompletionStartedEvent;
use App\Event\Campaign\CampaignDraftCreatedEvent;
use App\Event\Campaign\CampaignPendingEvent;
use App\Event\Campaign\RenewedCampaignEvent;
use App\Entity\Administrator;
use App\Entity\Creator;
use App\Entity\Manager;
use App\Entity\Sponsor;
use App\Event\Creator\BannedEvent;
use App\Event\Creator\CreatorReAuthRequiredEvent;
use App\Event\Creator\CreatorTokenRefreshedEvent;
use App\Event\Payment\CreatorPayoutFailedEvent;
use App\Event\Payment\CreatorPayoutSucceededEvent;
use App\Entity\Activity;
use App\Messenger\Analytics\PostHogCaptureMessage;
use App\Services\Analytics\PostHog\PostHogDistinctIdResolver;
use App\Services\Analytics\PostHog\PostHogEventMapper;
use App\Services\Analytics\PostHog\PostHogSessionResolver;
use App\Services\Administrator\Event\AdministratorCreatedEvent;
use App\Services\Manager\Event\ManagerCreatedEvent;
use App\Services\Sponsor\Event\SponsorCreatedEvent;
use App\Services\Sponsor\Event\SponsorLoggedInEvent;
use App\Services\Sponsor\Event\SponsorLoggedOutEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
use Symfony\Component\Security\Http\Event\LogoutEvent;
final class PostHogEventSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly MessageBusInterface $bus,
private readonly PostHogEventMapper $mapper,
private readonly PostHogDistinctIdResolver $distinctIdResolver,
private readonly PostHogSessionResolver $sessionResolver,
) {
}
public static function getSubscribedEvents(): array
{
return [
AdministratorCreatedEvent::class => 'onAdministratorCreated',
ManagerCreatedEvent::class => 'onManagerCreated',
SponsorCreatedEvent::class => 'onSponsorCreated',
SponsorLoggedInEvent::class => 'onSponsorLoggedIn',
SponsorLoggedOutEvent::class => 'onSponsorLoggedOut',
LoginSuccessEvent::class => 'onLoginSuccess',
LogoutEvent::class => 'onLogout',
CampaignDraftCreatedEvent::class => 'onCampaignDraftCreated',
ActivatedEvent::class => 'onCampaignActivated',
CampaignPendingEvent::class => 'onCampaignPending',
AcceptedEvent::class => 'onSubmissionApproved',
ReviewEvent::class => 'onReview',
ApproveAllReviewEvent::class => 'onApproveAllReview',
DeclineAllReviewEvent::class => 'onDeclineAllReview',
ActivityActivatedEvent::class => 'onVideoApproved',
ActivityDeclinedEvent::class => 'onActivityDeclined',
BannedEvent::class => 'onCreatorBlocked',
ActivityBoostCodeRequested::class => 'onBoostRequested',
ActivityBoostCodeReceived::class => 'onCreatorBoostCodeAdded',
CreatorCampaignAppliedEvent::class => 'onCreatorCampaignApplied',
CreatorVideoSubmittedEvent::class => 'onCreatorVideoSubmitted',
CreatorPayoutSucceededEvent::class => 'onCreatorCashOutSucceeded',
CreatorPayoutFailedEvent::class => 'onCreatorCashOutFailed',
PersonalBonusAddedEvent::class => 'onBonusAdded',
CampaignCompletionStartedEvent::class => 'onCampaignCompletionStarted',
RenewedCampaignEvent::class => 'onCampaignRestarted',
CampaignBudgetIncreasedEvent::class => 'onCampaignBudgetIncreased',
CreatorReAuthRequiredEvent::class => 'onCreatorReAuthRequired',
CreatorTokenRefreshedEvent::class => 'onCreatorTokenRefreshed',
];
}
public function onAdministratorCreated(AdministratorCreatedEvent $event): void
{
$administrator = $event->getAdministrator();
$administratorId = $administrator->getId();
if ($administratorId === null) {
return;
}
$this->dispatch($this->mapper->administratorCreated(
$administrator,
$this->distinctIdResolver->userDistinctId($administratorId),
));
}
public function onManagerCreated(ManagerCreatedEvent $event): void
{
$manager = $event->getManager();
$managerId = $manager->getId();
if ($managerId === null) {
return;
}
$this->dispatch($this->mapper->managerCreated(
$manager,
$this->distinctIdResolver->userDistinctId($managerId),
));
}
public function onSponsorCreated(SponsorCreatedEvent $event): void
{
$sponsor = $event->getSponsor();
$sponsorId = $sponsor->getId();
if ($sponsorId === null) {
return;
}
$this->dispatch($this->mapper->sponsorSignedUp(
$sponsor,
$this->distinctIdResolver->userDistinctId($sponsorId),
$event->getRegistrationChannel(),
));
}
public function onSponsorLoggedIn(SponsorLoggedInEvent $event): void
{
$this->dispatchSponsorLoggedIn($event->getSponsor(), $event->getLoginChannel());
}
public function onSponsorLoggedOut(SponsorLoggedOutEvent $event): void
{
$this->dispatchSponsorLoggedOut($event->getSponsor(), $event->getLogoutChannel());
}
public function onLogout(LogoutEvent $event): void
{
$token = $event->getToken();
$user = $token?->getUser();
if ($user instanceof Sponsor) {
$this->dispatchSponsorLoggedOut($user, $this->resolveLogoutChannel($token));
return;
}
if ($user instanceof Manager) {
$this->dispatchManagerLoggedOut($user, $this->resolveLogoutChannel($token));
return;
}
if ($user instanceof Administrator) {
$this->dispatchAdministratorLoggedOut($user, $this->resolveLogoutChannel($token));
return;
}
if ($user instanceof Creator) {
$this->dispatchCreatorLoggedOut($user, $this->resolveLogoutChannel($token));
}
}
public function onLoginSuccess(LoginSuccessEvent $event): void
{
$user = $event->getUser();
if ($user instanceof Sponsor) {
$loginChannel = match ($event->getFirewallName()) {
'sponsor' => 'web',
'sponsor_authentication' => 'api_legacy',
'api_sponsor' => 'api',
default => $event->getFirewallName(),
};
$this->dispatchSponsorLoggedIn($user, $loginChannel);
return;
}
if ($user instanceof Manager) {
$loginChannel = match ($event->getFirewallName()) {
'manager' => 'web',
default => $event->getFirewallName(),
};
$this->dispatchManagerLoggedIn($user, $loginChannel);
return;
}
if ($user instanceof Administrator) {
$loginChannel = match ($event->getFirewallName()) {
'backend' => 'web',
default => $event->getFirewallName(),
};
$this->dispatchAdministratorLoggedIn($user, $loginChannel);
return;
}
if ($user instanceof Creator) {
$loginChannel = match ($event->getFirewallName()) {
'creator' => 'web',
'api_creator', 'api_creator_front' => 'api',
default => $event->getFirewallName(),
};
$this->dispatchCreatorLoggedIn($user, $loginChannel);
}
}
public function onCampaignDraftCreated(CampaignDraftCreatedEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignDraftCreated(
$campaign,
$event->hasFullAccess(),
$event->getManagerId(),
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
));
}
public function onCampaignActivated(ActivatedEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignLaunched(
$campaign,
'active',
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
));
}
public function onCampaignPending(CampaignPendingEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignLaunched(
$campaign,
'pending',
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
));
}
public function onSubmissionApproved(AcceptedEvent $event): void
{
$activity = $event->getActivity();
$this->dispatch($this->mapper->submissionApproved(
$activity,
$this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId()),
));
}
public function onReview(ReviewEvent $event): void
{
$activity = $event->getActivity();
if (!$this->mapper->isSubmissionDeclineReviewEvent($activity)) {
return;
}
$this->dispatch($this->mapper->submissionDeclined(
$activity,
$this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId()),
));
}
public function onApproveAllReview(ApproveAllReviewEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->submissionApprovedAll(
$campaign,
$this->distinctIdResolver->systemDistinctId((int) $campaign->getSponsor()->getId()),
));
}
public function onDeclineAllReview(DeclineAllReviewEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->submissionDeclinedAll(
$campaign,
$event->getReason(),
$this->distinctIdResolver->systemDistinctId((int) $campaign->getSponsor()->getId()),
));
}
public function onVideoApproved(ActivityActivatedEvent $event): void
{
$activity = $event->getActivity();
$this->dispatch($this->mapper->videoApproved(
$activity,
$this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId()),
));
}
public function onActivityDeclined(ActivityDeclinedEvent $event): void
{
$activity = $event->getActivity();
$distinctId = $this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId());
if ($event->getDeclineContext() === ActivityDeclinedEvent::CONTEXT_VIDEO) {
$this->dispatch($this->mapper->videoDeclined($activity, $distinctId));
return;
}
$this->dispatch($this->mapper->submissionDeclined($activity, $distinctId));
}
public function onCreatorBlocked(BannedEvent $event): void
{
$creator = $event->getCreator();
$sponsorId = $this->resolveSponsorIdForBlock();
if ($sponsorId === null) {
return;
}
$this->dispatch($this->mapper->creatorBlocked(
$creator,
$sponsorId,
$this->distinctIdResolver->forActor($sponsorId),
$creator->getBlockedUntil(),
));
}
public function onBoostRequested(ActivityBoostCodeRequested $event): void
{
$activity = $event->getActivity();
$this->dispatch($this->mapper->creatorBoostRequested(
$activity,
$this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId()),
));
}
public function onCreatorBoostCodeAdded(ActivityBoostCodeReceived $event): void
{
$activity = $event->getActivity();
$this->dispatchCreatorActivityEvent($activity, fn (string $distinctId, ?Creator $identifyCreator) => $this->mapper->creatorBoostCodeAdded(
$activity,
$distinctId,
$identifyCreator,
));
}
public function onCreatorCampaignApplied(CreatorCampaignAppliedEvent $event): void
{
$activity = $event->getActivity();
$this->dispatchCreatorActivityEvent($activity, fn (string $distinctId, ?Creator $identifyCreator) => $this->mapper->creatorCampaignApplied(
$activity,
$event->isAutoAccepted(),
$distinctId,
$identifyCreator,
));
}
public function onCreatorVideoSubmitted(CreatorVideoSubmittedEvent $event): void
{
$activity = $event->getActivity();
$this->dispatchCreatorActivityEvent($activity, fn (string $distinctId, ?Creator $identifyCreator) => $this->mapper->creatorVideoSubmitted(
$activity,
$event->getSubmissionPath(),
$distinctId,
$identifyCreator,
));
}
public function onCreatorCashOutSucceeded(CreatorPayoutSucceededEvent $event): void
{
$creator = $event->getCreator();
if ($creator->getId() === null) {
return;
}
$identifyCreator = $this->distinctIdResolver->resolveCreatorForCurrentActor($creator);
$distinctId = $this->distinctIdResolver->resolveCreatorDistinctIdForCurrentActor($creator);
if ($distinctId === null) {
return;
}
$this->dispatch($this->mapper->creatorCashOutSucceeded(
$creator,
$event->getAmount(),
$event->getPaymentSystem(),
$event->getChannel(),
$distinctId,
$identifyCreator,
));
}
public function onCreatorCashOutFailed(CreatorPayoutFailedEvent $event): void
{
$creator = $event->getCreator();
if ($creator->getId() === null) {
return;
}
$identifyCreator = $this->distinctIdResolver->resolveCreatorForCurrentActor($creator);
$distinctId = $this->distinctIdResolver->resolveCreatorDistinctIdForCurrentActor($creator);
if ($distinctId === null) {
return;
}
$this->dispatch($this->mapper->creatorCashOutFailed(
$creator,
$event->getPaymentSystem(),
$event->getChannel(),
$event->getFailureReason(),
$distinctId,
$event->getAmount(),
$identifyCreator,
));
}
public function onBonusAdded(PersonalBonusAddedEvent $event): void
{
$activity = $event->getActivity();
$this->dispatch($this->mapper->bonusAdded(
$activity,
$event->getAmount(),
$this->distinctIdResolver->forActor($activity->getCampaign()->getSponsor()->getId()),
));
}
public function onCampaignCompletionStarted(CampaignCompletionStartedEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignCompletionStarted(
$campaign,
$event->shouldNotifySponsor(),
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
));
}
public function onCampaignRestarted(RenewedCampaignEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignRestarted(
$campaign,
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
$campaign->getAmount(),
$campaign->getDeadlineAt(),
));
}
public function onCampaignBudgetIncreased(CampaignBudgetIncreasedEvent $event): void
{
$campaign = $event->getCampaign();
$this->dispatch($this->mapper->campaignBudgetIncreased(
$campaign,
$event->getAmount(),
$this->distinctIdResolver->forActor($campaign->getSponsor()->getId()),
));
}
public function onCreatorReAuthRequired(CreatorReAuthRequiredEvent $event): void
{
$this->dispatchCreatorProfileEvent(
$event->getCreator(),
fn (string $distinctId, Creator $creator) => $this->mapper->creatorReAuthRequired(
$creator,
$distinctId,
$event->getPlatform(),
),
);
}
public function onCreatorTokenRefreshed(CreatorTokenRefreshedEvent $event): void
{
$this->dispatchCreatorProfileEvent(
$event->getCreator(),
fn (string $distinctId, Creator $creator) => $this->mapper->creatorTokenRefreshed(
$creator,
$distinctId,
$event->getPlatform(),
),
);
}
private function dispatch(PostHogCaptureMessage $message): void
{
$this->bus->dispatch(new PostHogCaptureMessage(
event: $message->getEvent(),
distinctId: $message->getDistinctId(),
properties: $message->getProperties(),
identifyProperties: $message->getIdentifyProperties(),
sessionId: $this->sessionResolver->resolveSessionId(),
));
}
private function dispatchSponsorLoggedIn(Sponsor $sponsor, string $loginChannel): void
{
$sponsorId = $sponsor->getId();
if ($sponsorId === null) {
return;
}
$this->dispatch($this->mapper->sponsorLoggedIn(
$sponsor,
$this->distinctIdResolver->userDistinctId($sponsorId),
$loginChannel,
));
}
private function dispatchSponsorLoggedOut(Sponsor $sponsor, string $logoutChannel): void
{
$sponsorId = $sponsor->getId();
if ($sponsorId === null) {
return;
}
$this->dispatch($this->mapper->sponsorLoggedOut(
$sponsor,
$this->distinctIdResolver->userDistinctId($sponsorId),
$logoutChannel,
));
}
private function dispatchManagerLoggedIn(Manager $manager, string $loginChannel): void
{
$managerId = $manager->getId();
if ($managerId === null) {
return;
}
$this->dispatch($this->mapper->managerLoggedIn(
$manager,
$this->distinctIdResolver->userDistinctId($managerId),
$loginChannel,
));
}
private function dispatchManagerLoggedOut(Manager $manager, string $logoutChannel): void
{
$managerId = $manager->getId();
if ($managerId === null) {
return;
}
$this->dispatch($this->mapper->managerLoggedOut(
$manager,
$this->distinctIdResolver->userDistinctId($managerId),
$logoutChannel,
));
}
private function dispatchAdministratorLoggedIn(Administrator $administrator, string $loginChannel): void
{
$administratorId = $administrator->getId();
if ($administratorId === null) {
return;
}
$this->dispatch($this->mapper->administratorLoggedIn(
$administrator,
$this->distinctIdResolver->userDistinctId($administratorId),
$loginChannel,
));
}
private function dispatchAdministratorLoggedOut(Administrator $administrator, string $logoutChannel): void
{
$administratorId = $administrator->getId();
if ($administratorId === null) {
return;
}
$this->dispatch($this->mapper->administratorLoggedOut(
$administrator,
$this->distinctIdResolver->userDistinctId($administratorId),
$logoutChannel,
));
}
private function dispatchCreatorLoggedIn(Creator $creator, string $loginChannel): void
{
$distinctId = $this->distinctIdResolver->resolveCreatorDistinctIdForCurrentActor($creator);
if ($distinctId === null) {
return;
}
$this->dispatch($this->mapper->creatorLoggedIn(
$creator,
$distinctId,
$loginChannel,
));
}
private function dispatchCreatorLoggedOut(Creator $creator, string $logoutChannel): void
{
$distinctId = $this->distinctIdResolver->resolveCreatorDistinctIdForCurrentActor($creator);
if ($distinctId === null) {
return;
}
$this->dispatch($this->mapper->creatorLoggedOut(
$creator,
$distinctId,
$logoutChannel,
));
}
private function resolveLogoutChannel(?TokenInterface $token): string
{
if ($token === null || !method_exists($token, 'getFirewallName')) {
return 'unknown';
}
return match ($token->getFirewallName()) {
'sponsor' => 'web',
'sponsor_authentication' => 'api_legacy',
'api_sponsor' => 'api',
'manager' => 'web',
'backend' => 'web',
'creator' => 'web',
default => $token->getFirewallName(),
};
}
private function resolveSponsorIdForBlock(): ?int
{
return $this->distinctIdResolver->resolveSponsorIdForCurrentActor();
}
/**
* @param callable(string, Creator): PostHogCaptureMessage $buildMessage
*/
private function dispatchCreatorProfileEvent(Creator $profileCreator, callable $buildMessage): void
{
if ($profileCreator->getId() === null) {
return;
}
$this->dispatch($buildMessage(
$this->distinctIdResolver->creatorDistinctId($profileCreator),
$profileCreator,
));
}
/**
* @param callable(string, ?Creator): PostHogCaptureMessage $buildMessage
*/
private function dispatchCreatorActivityEvent(Activity $activity, callable $buildMessage): bool
{
$profileCreator = $activity->getCreator();
$distinctId = $this->distinctIdResolver->resolveCreatorDistinctIdForCurrentActor($profileCreator);
if ($distinctId === null) {
return false;
}
$identifyCreator = $this->distinctIdResolver->resolveCreatorForCurrentActor($profileCreator);
$this->dispatch($buildMessage($distinctId, $identifyCreator));
return true;
}
}