line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3
|
|
|
3
|
|
134900
|
use v5.20; |
|
3
|
|
|
|
|
51
|
|
2
|
3
|
|
|
3
|
|
15
|
use warnings; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
94
|
|
3
|
3
|
|
|
3
|
|
17
|
use feature 'signatures'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
441
|
|
4
|
3
|
|
|
3
|
|
29
|
no warnings qw(experimental::signatures); |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
153
|
|
5
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
20
|
use Carp (); |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
50
|
|
7
|
3
|
|
|
3
|
|
2114
|
use JSON (); |
|
3
|
|
|
|
|
36919
|
|
|
3
|
|
|
|
|
2197
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = 4.01; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
package Net::Google::Analytics::MeasurementProtocol { |
12
|
|
|
|
|
|
|
|
13
|
0
|
|
|
0
|
1
|
0
|
sub new ($class, %args) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
14
|
|
|
|
|
|
|
return bless { |
15
|
|
|
|
|
|
|
api_secret => $args{api_secret}, |
16
|
|
|
|
|
|
|
measurement_id => $args{measurement_id}, |
17
|
|
|
|
|
|
|
client_id => $args{client_id} // _gen_uuid_v4(), |
18
|
|
|
|
|
|
|
agent => $args{agent} // _build_user_agent(), |
19
|
|
|
|
|
|
|
debug => $args{debug}, |
20
|
0
|
|
0
|
|
|
0
|
_route => _build_route(%args), |
|
|
|
0
|
|
|
|
|
21
|
|
|
|
|
|
|
}, $class; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
0
|
|
|
0
|
1
|
0
|
sub send ($self, $name, $properties) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
25
|
0
|
0
|
|
|
|
0
|
Carp::croak('properties must be a hashref') unless ref $properties eq 'HASH'; |
26
|
0
|
|
|
|
|
0
|
return $self->send_multiple( [{ $name => $properties }] ); |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
0
|
|
|
0
|
1
|
0
|
sub send_multiple ($self, $events) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
30
|
0
|
0
|
|
|
|
0
|
Carp::croak('events must be an array reference') unless ref $events eq 'ARRAY'; |
31
|
0
|
|
|
|
|
0
|
my @formatted_events; |
32
|
0
|
|
|
|
|
0
|
foreach my $e (@$events) { |
33
|
0
|
|
|
|
|
0
|
my ($name, $params) = each %$e; |
34
|
0
|
|
|
|
|
0
|
push @formatted_events, { name => $name, params => $params } |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my $payload = JSON::encode_json({ |
38
|
|
|
|
|
|
|
client_id => $self->{client_id}, |
39
|
0
|
|
|
|
|
0
|
events => \@formatted_events, |
40
|
|
|
|
|
|
|
}); |
41
|
|
|
|
|
|
|
|
42
|
0
|
0
|
|
|
|
0
|
my $res = $self->{agent}->post( $self->{_route}, $self->{agent}->isa('Furl') ? undef : (), $payload ); |
43
|
0
|
0
|
|
|
|
0
|
if ($res->is_success) { |
44
|
0
|
0
|
|
|
|
0
|
return $self->{debug} ? JSON::decode_json($res->decoded_content) : 1; |
45
|
|
|
|
|
|
|
} |
46
|
0
|
|
|
|
|
0
|
return { __PACKAGE__ => $res->decoded_content }; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
0
|
|
|
0
|
|
0
|
sub _build_route(%args) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
50
|
0
|
0
|
|
|
|
0
|
if ($args{tid}) { |
51
|
0
|
|
|
|
|
0
|
Carp::croak('Looks like you are calling ' . __PACKAGE__ . ' with' |
52
|
|
|
|
|
|
|
. ' outdated arguments from Universal Analytics. Please update' |
53
|
|
|
|
|
|
|
. ' to Google Analytics 4 (GA4) accordingly'); |
54
|
|
|
|
|
|
|
} |
55
|
0
|
0
|
|
|
|
0
|
if (!$args{api_secret}) { |
56
|
0
|
|
|
|
|
0
|
Carp::croak('api_secret is required. Create one in Admin > Data Streams' |
57
|
|
|
|
|
|
|
. ' > choose your stream > Measurement Protocol > Create'); |
58
|
|
|
|
|
|
|
} |
59
|
0
|
0
|
|
|
|
0
|
if (!$args{measurement_id}) { |
60
|
0
|
|
|
|
|
0
|
Carp::croak('measurement_id is required. Find yours under Admin > Data' |
61
|
|
|
|
|
|
|
. ' Streams > choose your stream > Measurement ID'); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
0
|
0
|
|
|
|
0
|
my $debug = $args{debug} ? '/debug' : ''; |
65
|
|
|
|
|
|
|
return 'https://www.google-analytics.com' . $debug . '/mp/collect' |
66
|
|
|
|
|
|
|
. '?measurement_id=' . $args{measurement_id} |
67
|
0
|
|
|
|
|
0
|
. '&api_secret=' . $args{api_secret}; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _build_user_agent { |
71
|
0
|
|
|
0
|
|
0
|
require Furl; |
72
|
0
|
|
|
|
|
0
|
return Furl->new( agent => __PACKAGE__ . '/' . $VERSION, timeout => 5, headers => ['Content-Type' => 'application/json'] ); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# UUID v4 (pseudo-random) generator based on UUID::Tiny |
76
|
|
|
|
|
|
|
sub _gen_uuid_v4 { |
77
|
2
|
|
|
2
|
|
82
|
my $uuid = ''; |
78
|
2
|
|
|
|
|
8
|
for ( 1 .. 4 ) { |
79
|
8
|
|
|
|
|
24
|
my ($v1, $v2) = (int(rand(65536)) % 65536, int(rand(65536)) % 65536); |
80
|
8
|
|
|
|
|
14
|
my $rand_32bit = ($v1 << 16) | $v2; |
81
|
8
|
|
|
|
|
22
|
$uuid .= pack 'I', $rand_32bit; |
82
|
|
|
|
|
|
|
} |
83
|
2
|
|
|
|
|
9
|
substr $uuid, 6, 1, chr( ord( substr( $uuid, 6, 1 ) ) & 0x0f | 0x40 ); |
84
|
2
|
|
|
|
|
5
|
substr $uuid, 8, 1, chr( ord( substr( $uuid, 8, 1 ) ) & 0x3f | 0x80 ); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# uuid is created. Convert to string: |
87
|
2
|
|
|
|
|
5
|
return join '-', map { unpack 'H*', $_ } map { substr $uuid, 0, $_, '' } ( 4, 2, 2, 2, 6 ); |
|
10
|
|
|
|
|
32
|
|
|
10
|
|
|
|
|
22
|
|
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
}; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
1; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
__END__ |