sidekick-1.0.x-dev/src/SidekickService.php
src/SidekickService.php
<?php
namespace Drupal\sidekick;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\Renderer;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\TransferException;
use GuzzleHttp\RequestOptions;
/**
* Service for HTTP request.
*/
class SidekickService {
/**
* The HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The sidekick config.
*
* @var \Drupal\Core\Config\Config
*/
protected $configFactory;
/**
* Language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Renderer.
*
* @var \Drupal\Core\Render\Renderer
*/
protected $renderer;
/**
* Renderer.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* CacheFactory.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cacheFactory;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* Status of the API Key.
*
* @var string
*/
protected $keyStatusApi;
/**
* Account API URL.
*
* @var string
*/
protected $accountApi;
/**
* Module API URL.
*
* @var string
*/
protected $accountUsageApi;
/**
* Account Usage API URL.
*
* @var string
*/
protected $modulesListApi;
/**
* Language API URl.
*
* @var string
*/
protected $languagesListApi;
/**
* Writing style API URL.
*
* @var string
*/
protected $writingStyleListApi;
/**
* Chat API URL.
*
* @var string
*/
protected $chatApi;
/**
* API Key value.
*
* @var string
*/
protected $key;
/**
* Constructor.
*
* @param \GuzzleHttp\ClientInterface $httpClient
* Guzzle Http client for requests.
* @param \Drupal\Core\Session\AccountInterface $currentUser
* Logged in User object.
* @param \Drupal\Core\Config\ConfigFactory $configFactory
* The factory for configuration objects.
* @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
* The language manager service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Render\Renderer $renderer
* The renderer service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $factory
* The LoggerChannelFactoryInterface object.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheFactory
* Cache backend instance to use.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
*/
public function __construct(
ClientInterface $httpClient,
AccountInterface $currentUser,
ConfigFactory $configFactory,
LanguageManagerInterface $languageManager,
EntityTypeManagerInterface $entity_type_manager,
Renderer $renderer,
LoggerChannelFactoryInterface $factory,
CacheBackendInterface $cacheFactory,
TimeInterface $time,
MessengerInterface $messenger,
) {
$this->httpClient = $httpClient;
$this->currentUser = $currentUser;
$this->configFactory = $configFactory;
$this->languageManager = $languageManager;
$this->entityTypeManager = $entity_type_manager;
$this->renderer = $renderer;
$this->loggerFactory = $factory;
$this->cacheFactory = $cacheFactory;
$this->keyStatusApi = 'https://assistant.ai-sidekick.app/api/v1/apikey-status';
$this->accountApi = 'https://assistant.ai-sidekick.app/api/v1/accounts/current';
$this->accountUsageApi = 'https://assistant.ai-sidekick.app/api/v1/accounts/usage';
$this->modulesListApi = 'https://assistant.ai-sidekick.app/api/v1/modules';
$this->languagesListApi = 'https://assistant.ai-sidekick.app/api/v1/languages';
$this->writingStyleListApi = 'https://assistant.ai-sidekick.app/api/v1/writing-styles';
$this->chatApi = 'https://assistant.ai-sidekick.app/api/v1/chat';
$this->key = $this->configFactory->get('sidekick.settings')->get(
'api_key'
);
$this->time = $time;
$this->messenger = $messenger;
}
/**
* To Check status of Key.
*
* @param string $language_code
* Language code for key.
* @param string|null $key
* Key whose status need to check.
* @param bool|null $reset_cache
* Need to reset cache. Yes or No.
*
* @return array|mixed
* Returns of data or exception.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function checkKeyStatus($language_code, ?string $key = '', ?bool $reset_cache = FALSE) {
if (empty($key)) {
$key = $this->key;
}
if (empty($key)) {
$build_message['message'] = [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => t(
'You have not added your Sidekick api key. Please add it.'
),
];
$build_message['link'] = [
'#type' => 'link',
'#title' => t('here'),
'#url' => Url::fromRoute('sidekick.settings_form'),
];
$message = $this->renderer->render($build_message);
$this->messenger->addWarning($message);
return [];
}
$data = $this->cacheFactory->get('key_status');
if (!$reset_cache && $data) {
$keyStatus = $data->data;
}
else {
try {
$response = $this->httpClient->post(
$this->keyStatusApi, [
'headers' => [
'Content-Type' => 'application/json',
],
'body' => json_encode(
[
'token' => $key,
'language' => $language_code,
]
),
]
);
$keyStatus = [
'status' => $response->getStatusCode(),
'message' => json_decode($response->getBody()),
];
$this->cacheFactory->set(
'key_status',
$keyStatus,
$this->time->getRequestTime() + 86400
// Set Cache for day.
);
}
catch (ClientException $e) {
$keyStatus = [
'status' => $e->getCode(),
'message' => json_decode(
$e->getResponse()
->getBody()
->getContents()
)->message,
];
}
}
return $keyStatus;
}
/**
* Get possible modules from AI.
*
* @param string $language_code
* Language code in which Modules are needed.
*
* @return array|mixed
* Returns of data or exception.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getModules($language_code) {
$data = $this->cacheFactory->get('module_list');
if ($data) {
$modules = $data->data;
}
else {
try {
$response = $this->httpClient->get(
$this->modulesListApi, [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->key,
],
'body' => json_encode(
[
'language' => $language_code,
]
),
]
);
$modules = [
'status' => $response->getStatusCode(),
'message' => json_decode($response->getBody()),
];
$this->cacheFactory->set(
'module_list',
$modules,
$this->time->getRequestTime() + 86400
// Set Cache for day.
);
}
catch (ClientException $e) {
$modules = [
'status' => $e->getCode(),
'message' => json_decode(
$e->getResponse()
->getBody()
->getContents()
)->message,
];
}
}
return $modules;
}
/**
* Get possible language options from AI.
*
* @param string $language_code
* Language code.
*
* @return array|mixed
* Returns of data or exception.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getLanguages($language_code) {
try {
if ($cache = $this->cacheFactory->get('language_list')) {
return $cache->data;
}
else {
$response = $this->httpClient->get(
$this->languagesListApi, [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->key,
],
'body' => json_encode(
[
'language' => $language_code,
]
),
]
);
$data = [
'status' => $response->getStatusCode(),
'message' => json_decode($response->getBody()),
];
$this->cacheFactory->set(
'language_list',
$data,
$this->time->getRequestTime() + 86400
// Set Cache for day.
);
return $data;
}
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return (object) [
'message' => $exception->getMessage(),
];
}
}
/**
* Get Writing Style from AI source.
*
* @param string $language_code
* The Language code in which Writing Style needed.
*
* @return array|mixed
* Returns of data or exception.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getWritingStyles($language_code) {
try {
if ($cache = $this->cacheFactory->get('writing_style')) {
return $cache->data;
}
else {
$response = $this->httpClient->get(
$this->writingStyleListApi,
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->key,
],
'body' => json_encode(
[
'language' => $language_code,
]
),
]
);
$data = [
'status' => $response->getStatusCode(),
'message' => json_decode($response->getBody()),
];
$this->cacheFactory->set(
'writing_style',
$data,
$this->time->getRequestTime() + 86400
// Set Cache for day.
);
return $data;
}
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return (object) [
'message' => $exception->getMessage(),
];
}
}
/**
* Get Data from AI.
*
* @param string $sidekick_module
* The module type of which we are requesting data.
* @param string $language_code
* The Language code for AI.
* @param string $writing_style
* The writing style in which data need to fetch.
* @param string $salutation
* The Type of salutation.
* @param string $user_input
* User input in JSON format.
*
* @return array|mixed
* Returns of data or exception.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getChat(
$sidekick_module,
$language_code,
$writing_style,
$salutation,
$user_input,
) {
try {
$response = $this->httpClient->post(
$this->chatApi, [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->key,
],
'query' => ['language' => $language_code],
'body' => json_encode(
[
'module' => $sidekick_module ?? 'page_title_writer',
'writing_style' => $writing_style,
'salutation' => $salutation,
'user_input' => $user_input,
]
),
]
);
return [
'status' => $response->getStatusCode(),
'message' => json_decode($response->getBody()),
];
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return [
'status' => $exception->getCode(),
'message' => $exception->getMessage(),
];
}
}
/**
* Fetch the Context of Account from AI source.
*
* @param string $key
* API key for AI requests.
*
* @return array|mixed
* Returns array.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getAccountContext($key) {
try {
$response = $this->httpClient->get(
$this->accountApi, [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $key,
],
]
);
return [
'status' => $response->getStatusCode(),
'message' => Json::decode($response->getBody()),
];
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return (object) [
'message' => $exception->getMessage(),
];
}
}
/**
* Fetch the Account Usages data from AI source.
*
* @param string $key
* API key for AI requests.
*
* @return array|mixed
* Returns array.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function getAccountUsage($key) {
try {
$response = $this->httpClient->get(
$this->accountUsageApi, [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $key,
],
]
);
return [
'status' => $response->getStatusCode(),
'message' => Json::decode($response->getBody()),
];
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return (object) [
'message' => $exception->getMessage(),
];
}
}
/**
* Set the Account context to AI source.
*
* @param string $key
* API key for AI request.
* @param string $page_briefing
* Briefing data for AI request.
* @param string $target_audience
* Target Audience for AI request.
*
* @return array|mixed
* Returns array.
*
* @throws \GuzzleHttp\Exception\GuzzleException
* Throws Exception.
*/
public function setAccountContext($key, $page_briefing, $target_audience) {
try {
$request_options = [];
$request_options[RequestOptions::HEADERS] = [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $key,
];
$request_options[RequestOptions::BODY] = Json::encode(
[
'context' => $page_briefing,
'target_audience' => $target_audience,
"process_web_page_context" => TRUE,
]
);
$response = $this->httpClient->request(
'put',
$this->accountApi,
$request_options
);
return [
'status' => $response->getStatusCode(),
'message' => Json::decode($response->getBody()),
];
}
catch (ClientException | RequestException | TransferException | BadResponseException $exception) {
$this->loggerFactory->get('sidekick')->error($exception);
return (object) [
'message' => $exception->getMessage(),
];
}
}
/**
* To Prepare and return the Data of Node.
*
* @param object $node
* Node Object.
* @param \Drupal\Core\Form\FormStateInterface|null $form_state
* FormState object.
*
* @return false|string
* Return either Form or JSON String
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getSideKickMessagingApiData($node, $form_state) {
$nodeUrl = Url::fromRoute(
'entity.node.canonical',
['node' => $node->id()],
['absolute' => TRUE]
)->toString();
$entity_type = $form_state->getFormObject()->getEntity()
->getEntityTypeId();
$node = $this->entityTypeManager->getStorage($entity_type)
->load($node->id());
$builder = $this->entityTypeManager->getViewBuilder($entity_type);
$build = $builder->view($node, 'default');
$output = $this->renderer->render($build);
$form_fields = $form_state->getFormObject()->getEntity()
->getFields();
$structuredContent = '';
foreach ($form_fields as $key => $field) {
if ($form_state->getFormObject()->getEntity()->get($key)->entity
) {
$structuredContent[$key] = $form_state->getFormObject()
->getEntity()->get($key)->entity;
}
else {
$structuredContent[$key] = $form_state->getFormObject()
->getEntity()->get($key)->value;
}
}
$sideKickMessagingApiData = [
'version' => '1.0',
'eventName' => 'page-changed',
'data' => [
'url' => $nodeUrl,
'title' => $node->getTitle(),
'content' => $output,
'structuredContent' => $structuredContent,
'targetAudience' => 'Tech-savvy people',
'pageBriefing' => '',
],
];
return json_encode($sideKickMessagingApiData);
}
/**
* Converts Query parameters in to array.
*
* @param string $language_code
* The Language Code.
*
* @return array
* Query Parameters in Array.
*/
public function getQueryParameter($language_code) {
$domain = Url::fromRoute(
'<front>',
[],
['absolute' => TRUE]
)->toString();
return [
'contentLanguage' => $language_code,
'interfaceLanguage' => $language_code,
'language' => $language_code,
'userId' => $this->currentUser->getDisplayName(),
'domain' => $domain,
'apikey' => $this->configFactory->get('sidekick.settings')->get(
'api_key'
),
'platform' => 'Drupal',
'hide_account_context' => TRUE,
'referral' => 'Acolono',
];
}
}
