Skip to content

Commit 21c365b

Browse files
committed
Force new visit if AI agent information changes
1 parent e7a3602 commit 21c365b

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

plugins/AIAgents/Columns/AIAgentName.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111

1212
namespace Piwik\Plugins\AIAgents\Columns;
1313

14+
use Piwik\Container\StaticContainer;
15+
use Piwik\Log\LoggerInterface;
1416
use Piwik\Plugin\Dimension\VisitDimension;
1517
use Piwik\Plugins\AIAgents\AIAgents;
18+
use Piwik\Tracker\Action;
1619
use Piwik\Tracker\Request;
1720
use Piwik\Tracker\Visitor;
1821

@@ -38,7 +41,40 @@ public function __construct()
3841
};
3942
}
4043

44+
public function shouldForceNewVisit(Request $request, Visitor $visitor, ?Action $action = null)
45+
{
46+
$previousProvider = $visitor->getVisitorColumn($this->columnName);
47+
$provider = $this->detectProvider($request);
48+
49+
if ($provider !== $previousProvider) {
50+
StaticContainer::get(LoggerInterface::class)->debug(
51+
'Existing visit detected, but creating new visit because AI agent information is different than last action.'
52+
);
53+
54+
return true;
55+
}
56+
57+
return false;
58+
}
59+
60+
/**
61+
* @return false
62+
*/
63+
public function onExistingVisit(Request $request, Visitor $visitor, $action)
64+
{
65+
// function implementation required to have the column available in shouldForceNewVisit.
66+
return false;
67+
}
68+
69+
/**
70+
* @return false|string
71+
*/
4172
public function onNewVisit(Request $request, Visitor $visitor, $action)
73+
{
74+
return $this->detectProvider($request);
75+
}
76+
77+
private function detectProvider(Request $request): ?string
4278
{
4379
$providers = AIAgents::getAvailableAgentProviders();
4480

@@ -48,6 +84,6 @@ public function onNewVisit(Request $request, Visitor $visitor, $action)
4884
}
4985
}
5086

51-
return false;
87+
return null;
5288
}
5389
}

plugins/AIAgents/tests/Integration/Columns/AIAgentNameTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testOnNewVisitShouldBeAbleToNotDetectAIAgent(): void
5959

6060
$result = $this->aiAgentName->onNewVisit($request, $visitor, null);
6161

62-
self::assertFalse($result);
62+
self::assertNull($result);
6363
}
6464

6565
/**
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
/**
4+
* Matomo - free/libre analytics platform
5+
*
6+
* @link https://matomo.org
7+
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8+
*/
9+
10+
namespace Piwik\Plugins\AIAgents\tests\Integration;
11+
12+
use MatomoTracker;
13+
use Piwik\Date;
14+
use Piwik\Plugins\AIAgents\Providers\ChatGPT as ChatGPTAgent;
15+
use Piwik\Plugins\Live\API as LiveAPI;
16+
use Piwik\Tests\Framework\Fixture;
17+
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
18+
19+
/**
20+
* @group AIAgents
21+
* @group Plugins
22+
*/
23+
class ForceNewVisitTest extends IntegrationTestCase
24+
{
25+
/**
26+
* @var int
27+
*/
28+
private $idSite = null;
29+
30+
/**
31+
* @var Date
32+
*/
33+
private $testDate;
34+
35+
/**
36+
* @var MatomoTracker
37+
*/
38+
protected $tracker = null;
39+
40+
public function setUp(): void
41+
{
42+
parent::setUp();
43+
44+
$this->testDate = Date::factory('now')->setTime('12:00:00');
45+
46+
$this->idSite = Fixture::createWebsite('2025-07-18 00:00:00');
47+
$this->tracker = Fixture::getTracker($this->idSite, $this->testDate->getDatetime());
48+
}
49+
50+
public function testTrackingWithChangedAgentCausesNewVisits(): void
51+
{
52+
$this->trackAgentPageView();
53+
$this->assertVisits(1, 1);
54+
$this->moveTimeForward(0.05);
55+
$this->trackAgentPageView();
56+
$this->assertVisits(1, 2);
57+
58+
$this->moveTimeForward(0.1);
59+
$this->trackHumanPageView();
60+
$this->assertVisits(2, 3);
61+
$this->moveTimeForward(0.15);
62+
$this->trackHumanPageView();
63+
$this->assertVisits(2, 4);
64+
65+
$this->moveTimeForward(0.2);
66+
$this->trackAgentPageView();
67+
$this->assertVisits(3, 5);
68+
$this->moveTimeForward(0.25);
69+
$this->trackAgentPageView();
70+
$this->assertVisits(3, 6);
71+
}
72+
73+
private function assertVisits(int $visitsExpected, int $actionsExpected): void
74+
{
75+
$counters = LiveAPI::getInstance()->getCounters(
76+
$this->idSite,
77+
2880,
78+
false,
79+
['visits', 'actions']
80+
);
81+
82+
self::assertEquals($visitsExpected, $counters[0]['visits']);
83+
self::assertEquals($actionsExpected, $counters[0]['actions']);
84+
}
85+
86+
private function moveTimeForward(float $hoursForward): void
87+
{
88+
$this->tracker->setForceVisitDateTime(
89+
$this->testDate->addHour($hoursForward)->getDatetime()
90+
);
91+
}
92+
93+
private function trackAgentPageView(): void
94+
{
95+
$this->tracker->setCustomTrackingParameter(ChatGPTAgent::PARAM_SIGNATURE, 'value irrelevant');
96+
$this->tracker->setCustomTrackingParameter(ChatGPTAgent::PARAM_SIGNATURE_AGENT, '"https://chatgpt.com"');
97+
$this->tracker->setCustomTrackingParameter(ChatGPTAgent::PARAM_SIGNATURE_INPUT, 'value irrelevant');
98+
99+
$this->tracker->setUrl('http://www.example.org/agent');
100+
101+
Fixture::checkResponse(
102+
$this->tracker->doTrackPageView('Agent - ' . $this->testDate->getDatetime())
103+
);
104+
}
105+
106+
private function trackHumanPageView(): void
107+
{
108+
$this->tracker->setUrl('http://www.example.org/human');
109+
110+
Fixture::checkResponse(
111+
$this->tracker->doTrackPageView('Human - ' . $this->testDate->getDatetime())
112+
);
113+
}
114+
}

0 commit comments

Comments
 (0)