Fixes #6007 -- Implement Google Analytics Measurement V4.
This commit is contained in:
parent
415e19e15d
commit
6ef72c643e
|
@ -29,6 +29,7 @@
|
|||
"azuracast/nowplaying": "dev-main",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"beberlei/doctrineextensions": "^1.2",
|
||||
"br33f/php-ga4-mp": "^0.1.2",
|
||||
"brick/math": "^0.10",
|
||||
"composer/ca-bundle": "^1.2",
|
||||
"doctrine/annotations": "^1.6",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4c004f346864c42ace26a2bc076ca89c",
|
||||
"content-hash": "99fa37d49611c4023e50cbe3c4ce9dee",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
|
@ -331,6 +331,53 @@
|
|||
},
|
||||
"time": "2020-11-29T07:37:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "br33f/php-ga4-mp",
|
||||
"version": "v0.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/br33f/php-GA4-Measurement-Protocol.git",
|
||||
"reference": "e9b95e5b0cf4daf05c3739d989867f1103835acb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/br33f/php-GA4-Measurement-Protocol/zipball/e9b95e5b0cf4daf05c3739d989867f1103835acb",
|
||||
"reference": "e9b95e5b0cf4daf05c3739d989867f1103835acb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.5.5 || ^7.0.0",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.14",
|
||||
"php-coveralls/php-coveralls": "^2.4",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Br33f\\Ga4\\MeasurementProtocol\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Damian Zamojski",
|
||||
"email": "damian.zamojski1@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP GoogleAnalytics4 Measurement Protocol Library",
|
||||
"support": {
|
||||
"issues": "https://github.com/br33f/php-GA4-Measurement-Protocol/issues",
|
||||
"source": "https://github.com/br33f/php-GA4-Measurement-Protocol/tree/v0.1.2"
|
||||
},
|
||||
"time": "2022-08-25T12:01:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.10.2",
|
||||
|
|
|
@ -70,9 +70,15 @@ return [
|
|||
'description' => __('Automatically publish to a Mastodon instance.'),
|
||||
'triggers' => $allTriggersExceptListeners,
|
||||
],
|
||||
Connector\GoogleAnalytics::NAME => [
|
||||
'class' => Connector\GoogleAnalytics::class,
|
||||
'name' => __('Google Analytics Integration'),
|
||||
Connector\GoogleAnalyticsV3::NAME => [
|
||||
'class' => Connector\GoogleAnalyticsV3::class,
|
||||
'name' => __('Google Analytics V3 Integration'),
|
||||
'description' => __('Send stream listener details to Google Analytics.'),
|
||||
'triggers' => [],
|
||||
],
|
||||
Connector\GoogleAnalyticsV4::NAME => [
|
||||
'class' => Connector\GoogleAnalyticsV4::class,
|
||||
'name' => __('Google Analytics V4 Integration'),
|
||||
'description' => __('Send stream listener details to Google Analytics.'),
|
||||
'triggers' => [],
|
||||
],
|
||||
|
|
|
@ -51,7 +51,8 @@ import Tunein from "./Form/Tunein";
|
|||
import Discord from "./Form/Discord";
|
||||
import Telegram from "./Form/Telegram";
|
||||
import Twitter from "./Form/Twitter";
|
||||
import GoogleAnalytics from "./Form/GoogleAnalytics";
|
||||
import GoogleAnalyticsV3 from "./Form/GoogleAnalyticsV3";
|
||||
import GoogleAnalyticsV4 from "./Form/GoogleAnalyticsV4";
|
||||
import MatomoAnalytics from "./Form/MatomoAnalytics";
|
||||
import Mastodon from "./Form/Mastodon";
|
||||
import {baseEditModalProps, useBaseEditModal} from "~/functions/useBaseEditModal";
|
||||
|
@ -294,7 +295,7 @@ const webhookConfig = {
|
|||
}
|
||||
},
|
||||
'google_analytics': {
|
||||
component: GoogleAnalytics,
|
||||
component: GoogleAnalyticsV3,
|
||||
validations: {
|
||||
tracking_id: {required}
|
||||
},
|
||||
|
@ -302,6 +303,17 @@ const webhookConfig = {
|
|||
tracking_id: ''
|
||||
}
|
||||
},
|
||||
'google_analytics_v4': {
|
||||
component: GoogleAnalyticsV4,
|
||||
validations: {
|
||||
api_secret: {required},
|
||||
measurement_id: {required}
|
||||
},
|
||||
defaultConfig: {
|
||||
api_secret: '',
|
||||
measurement_id: ''
|
||||
}
|
||||
},
|
||||
'matomo_analytics': {
|
||||
component: MatomoAnalytics,
|
||||
validations: {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<b-form-group>
|
||||
<div class="form-row">
|
||||
<b-wrapped-form-group
|
||||
id="form_config_api_secret"
|
||||
class="col-md-6"
|
||||
:field="form.config.api_secret"
|
||||
>
|
||||
<template #label>
|
||||
{{ $gettext('Measurement Protocol API Secret') }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ $gettext('This can be generated in the "Events" section for a measurement.') }}
|
||||
</template>
|
||||
</b-wrapped-form-group>
|
||||
|
||||
<b-wrapped-form-group
|
||||
id="form_config_measurement_id"
|
||||
class="col-md-6"
|
||||
:field="form.config.measurement_id"
|
||||
>
|
||||
<template #label>
|
||||
{{ $gettext('Measurement ID') }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ $gettext('A unique identifier (i.e. "G-A1B2C3D4") for this measurement stream.') }}
|
||||
</template>
|
||||
</b-wrapped-form-group>
|
||||
</div>
|
||||
</b-form-group>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -30,7 +30,7 @@ class StationHlsStream implements
|
|||
protected int $station_id;
|
||||
|
||||
#[
|
||||
ORM\ManyToOne(inversedBy: 'mounts'),
|
||||
ORM\ManyToOne(inversedBy: 'hls_streams'),
|
||||
ORM\JoinColumn(name: 'station_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')
|
||||
]
|
||||
protected Station $station;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity\Repository\ListenerRepository;
|
||||
use App\Entity\Station;
|
||||
use App\Nginx\CustomUrls;
|
||||
use GuzzleHttp\Client;
|
||||
use Monolog\Logger;
|
||||
|
||||
abstract class AbstractGoogleAnalyticsConnector extends AbstractConnector
|
||||
{
|
||||
public function __construct(
|
||||
Logger $logger,
|
||||
Client $httpClient,
|
||||
protected readonly ListenerRepository $listenerRepo
|
||||
) {
|
||||
parent::__construct($logger, $httpClient);
|
||||
}
|
||||
|
||||
protected function buildListenUrls(Station $station): array
|
||||
{
|
||||
$listenBaseUrl = CustomUrls::getListenUrl($station);
|
||||
$hlsBaseUrl = CustomUrls::getHlsUrl($station);
|
||||
|
||||
$mountUrls = [];
|
||||
foreach ($station->getMounts() as $mount) {
|
||||
$mountUrls[$mount->getIdRequired()] = $listenBaseUrl . $mount->getName();
|
||||
}
|
||||
|
||||
$remoteUrls = [];
|
||||
foreach ($station->getRemotes() as $remote) {
|
||||
$remoteUrls[$remote->getIdRequired()] = $listenBaseUrl . '/remote' . $remote->getMount();
|
||||
}
|
||||
|
||||
$hlsUrls = [];
|
||||
foreach ($station->getHlsStreams() as $hlsStream) {
|
||||
$hlsUrls[$hlsStream->getIdRequired()] = $hlsBaseUrl . '/' . $hlsStream->getName();
|
||||
}
|
||||
|
||||
return [
|
||||
'mounts' => $mountUrls,
|
||||
'remotes' => $remoteUrls,
|
||||
'hls' => $hlsUrls,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getListenUrl(
|
||||
array $listener,
|
||||
array $listenUrls
|
||||
): ?string {
|
||||
if (!empty($listener['mount_id'])) {
|
||||
return $listenUrls['mounts'][$listener['mount_id']] ?? null;
|
||||
}
|
||||
if (!empty($listener['remote_id'])) {
|
||||
return $listenUrls['remotes'][$listener['remote_id']] ?? null;
|
||||
}
|
||||
if (!empty($listener['hls_stream_id'])) {
|
||||
return $listenUrls['hls'][$listener['hls_stream_id']] ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -5,27 +5,15 @@ declare(strict_types=1);
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity\Api\NowPlaying\NowPlaying;
|
||||
use App\Entity\Repository\ListenerRepository;
|
||||
use App\Entity\Station;
|
||||
use App\Entity\StationWebhook;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Monolog\Logger;
|
||||
use TheIconic\Tracking\GoogleAnalytics\Analytics;
|
||||
use TheIconic\Tracking\GoogleAnalytics\Network\HttpClient;
|
||||
|
||||
final class GoogleAnalytics extends AbstractConnector
|
||||
final class GoogleAnalyticsV3 extends AbstractGoogleAnalyticsConnector
|
||||
{
|
||||
public const NAME = 'google_analytics';
|
||||
|
||||
public function __construct(
|
||||
Logger $logger,
|
||||
Client $httpClient,
|
||||
private readonly ListenerRepository $listenerRepo
|
||||
) {
|
||||
parent::__construct($logger, $httpClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
@ -41,21 +29,7 @@ final class GoogleAnalytics extends AbstractConnector
|
|||
}
|
||||
|
||||
// Get listen URLs for each mount point.
|
||||
$radioPort = $station->getFrontendConfig()->getPort();
|
||||
|
||||
$mountUrls = [];
|
||||
foreach ($station->getMounts() as $mount) {
|
||||
$mountUrl = (new Uri())
|
||||
->withPath('/radio/' . $radioPort . $mount->getName());
|
||||
$mountUrls[$mount->getId()] = (string)$mountUrl;
|
||||
}
|
||||
|
||||
$remoteUrls = [];
|
||||
foreach ($station->getRemotes() as $remote) {
|
||||
$remoteUrl = (new Uri())
|
||||
->withPath('/radio/remote' . $remote->getMount());
|
||||
$remoteUrls[$remote->getId()] = (string)$remoteUrl;
|
||||
}
|
||||
$listenUrls = $this->buildListenUrls($station);
|
||||
|
||||
// Build analytics
|
||||
$httpClient = new HttpClient();
|
||||
|
@ -72,13 +46,7 @@ final class GoogleAnalytics extends AbstractConnector
|
|||
|
||||
$i = 0;
|
||||
foreach ($liveListeners as $listener) {
|
||||
$listenerUrl = null;
|
||||
if (!empty($listener['mount_id'])) {
|
||||
$listenerUrl = $mountUrls[$listener['mount_id']] ?? null;
|
||||
} elseif (!empty($listener['remote_id'])) {
|
||||
$listenerUrl = $remoteUrls[$listener['remote_id']] ?? null;
|
||||
}
|
||||
|
||||
$listenerUrl = $this->getListenUrl($listener, $listenUrls);
|
||||
if (null === $listenerUrl) {
|
||||
continue;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity\Api\NowPlaying\NowPlaying;
|
||||
use App\Entity\Station;
|
||||
use App\Entity\StationWebhook;
|
||||
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
|
||||
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
|
||||
use Br33f\Ga4\MeasurementProtocol\HttpClient as Ga4HttpClient;
|
||||
use Br33f\Ga4\MeasurementProtocol\Service;
|
||||
|
||||
final class GoogleAnalyticsV4 extends AbstractGoogleAnalyticsConnector
|
||||
{
|
||||
public const NAME = 'google_analytics_v4';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function dispatch(
|
||||
Station $station,
|
||||
StationWebhook $webhook,
|
||||
NowPlaying $np,
|
||||
array $triggers
|
||||
): void {
|
||||
$config = $webhook->getConfig();
|
||||
|
||||
if (empty($config['api_secret']) || empty($config['measurement_id'])) {
|
||||
throw $this->incompleteConfigException(self::NAME);
|
||||
}
|
||||
|
||||
// Get listen URLs for each mount point.
|
||||
$listenUrls = $this->buildListenUrls($station);
|
||||
|
||||
// Build analytics
|
||||
$gaHttpClient = new Ga4HttpClient();
|
||||
$gaHttpClient->setClient($this->httpClient);
|
||||
|
||||
$ga4Service = new Service($config['api_secret'], $config['measurement_id']);
|
||||
$ga4Service->setHttpClient($gaHttpClient);
|
||||
|
||||
// Get all current listeners
|
||||
$liveListeners = $this->listenerRepo->iterateLiveListenersArray($station);
|
||||
|
||||
foreach ($liveListeners as $listener) {
|
||||
$listenerUrl = $this->getListenUrl($listener, $listenUrls);
|
||||
if (null === $listenerUrl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event = new BaseEvent('page_view');
|
||||
$event->setParamValue('page_location', $listenerUrl)
|
||||
->setParamValue('page_title', $listenerUrl)
|
||||
->setParamValue('ip', $listener['listener_ip'])
|
||||
->setParamValue('user_agent', $listener['listener_user_agent']);
|
||||
|
||||
$ga4Service->send(
|
||||
new BaseRequest(
|
||||
(string)$listener['listener_uid'],
|
||||
$event
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue