File: D:/web/matomo.vdk/plugins/Referrers/AIAssistant.php
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Referrers;
use Piwik\Cache;
use Piwik\Common;
use Piwik\Config;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\SettingsPiwik;
use Piwik\Singleton;
/**
* Contains methods to access AI assistant definition data.
*/
class AIAssistant extends Singleton
{
public const OPTION_STORAGE_NAME = 'AIAssistantDefinitions';
/** @var string location of definition file (relative to PIWIK_INCLUDE_PATH) */
public const DEFINITION_FILE = '/vendor/matomo/searchengine-and-social-list/AIAssistants.yml';
/** @var null|array<string, string> */
protected $definitionList = null;
/**
* Returns list of AI assistants by URL
*
* @return array<string, string>
*/
public function getDefinitions(): array
{
$cache = Cache::getEagerCache();
$cacheId = 'AIAssistant-' . self::OPTION_STORAGE_NAME;
if ($cache->contains($cacheId)) {
/** @var array<string, string> $list */
$list = $cache->fetch($cacheId);
} else {
$list = $this->loadDefinitions();
$cache->save($cacheId, $list);
}
return $list;
}
/**
* @return array<string, string>
*/
private function loadDefinitions(): array
{
if ($this->definitionList === null) {
$referrerDefinitionSyncOpt = Config::getInstance()->General['enable_referrer_definition_syncs'];
if ($referrerDefinitionSyncOpt == 1) {
$this->loadRemoteDefinitions();
} else {
$this->loadLocalYmlData();
}
}
Piwik::postEvent('Referrer.addAIAssistantUrls', [&$this->definitionList]);
return $this->definitionList ?? [];
}
/**
* Loads definitions sourced from remote yaml with a local fallback
*/
private function loadRemoteDefinitions(): void
{
// Read first from the auto-updated list in database
$list = Option::get(self::OPTION_STORAGE_NAME);
if ($list && SettingsPiwik::isInternetEnabled()) {
$list = Common::safe_unserialize(base64_decode($list));
if (!empty($list) && is_array($list)) {
$this->definitionList = $list;
}
} else {
// Fallback to reading the bundled list
$this->loadLocalYmlData();
Option::set(self::OPTION_STORAGE_NAME, base64_encode(serialize($this->definitionList)));
}
}
/**
* Loads the definition data from the local definitions file
*/
private function loadLocalYmlData(): void
{
$yml = file_get_contents(PIWIK_INCLUDE_PATH . self::DEFINITION_FILE);
if ($yml !== false) {
$this->definitionList = $this->loadYmlData($yml);
}
}
/**
* Parses the given YML string and caches the resulting definitions
*
* @return null|array<string, string>
*/
public function loadYmlData(string $yml): ?array
{
$ais = \Spyc::YAMLLoadString($yml);
if (is_array($ais)) {
$this->definitionList = $this->transformData($ais);
}
return $this->definitionList;
}
/**
* @param array<string, string[]> $ais
* @return array<string, string>
*/
protected function transformData(array $ais): array
{
$urlToName = [];
foreach ($ais as $name => $urls) {
if (empty($urls) || !is_array($urls)) {
continue;
}
foreach ($urls as $url) {
$urlToName[$url] = $name;
}
}
return $urlToName;
}
/**
* Returns true if a URL belongs to an AI assistant, false if otherwise.
*
* @param string $url The URL to check.
* @param string|null $aiAssistantName The name of the AI assistant to check for, or false to check for any.
*/
public function isAIAssistantUrl(string $url, ?string $aiAssistantName = null): bool
{
foreach ($this->getDefinitions() as $domain => $name) {
if (preg_match('#(^|[\.\/])' . preg_quote($domain) . '(\/|$)#', $url) && ($aiAssistantName === null || $name === $aiAssistantName)) {
return true;
}
}
return false;
}
/**
* Gets AI assistant name from URL.
*/
public function getAIAssistantFromDomain(string $url): string
{
foreach ($this->getDefinitions() as $domain => $name) {
if (preg_match('#(^|[\.\/])' . preg_quote($domain) . '(\/|$)#', $url)) {
return $name;
}
}
return Piwik::translate('General_Unknown');
}
/**
* Returns the main url of the AI assistant the given url matches
*/
public function getMainUrl(string $url): string
{
$ai = $this->getAIAssistantFromDomain($url);
foreach ($this->getDefinitions() as $domain => $name) {
if ($name === $ai) {
return $domain;
}
}
return $url;
}
/**
* Returns the main url of the given AI assistant
*/
public function getMainUrlFromName(string $aiAssistant): ?string
{
foreach ($this->getDefinitions() as $domain => $name) {
if ($name === $aiAssistant) {
return $domain;
}
}
return null;
}
/**
* Return AI assistant logo path by URL
*
* @see plugins/Morpheus/icons/dist/aiAssistants/
*/
public function getLogoFromUrl(string $url): string
{
$ai = $this->getAIAssistantFromDomain($url);
$ais = $this->getDefinitions();
$filePattern = 'plugins/Morpheus/icons/dist/aiAssistants/%s.png';
$aiDomains = array_keys($ais, $ai);
foreach ($aiDomains as $domain) {
if (file_exists(PIWIK_INCLUDE_PATH . '/' . sprintf($filePattern, $domain))) {
return sprintf($filePattern, $domain);
}
}
return sprintf($filePattern, 'xx');
}
}