HEX
Server: Apache
System: Linux cpanelx.inxs.ro 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: crowdandsafety (1041)
PHP: 8.1.34
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //proc/self/cwd/wp-content/plugins/shortpixel-image-optimiser/class/Controller/Api/AiController.php
<?php
namespace ShortPixel\Controller\Api;

use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
use ShortPixel\Controller\ApiKeyController as ApiKeyController;

use ShortPixel\Controller\Queue\QueueItems as QueueItems;
use \ShortPixel\Model\Queue\QueueItem as QueueItem;
use ShortPixel\Model\Image\ImageModel as ImageModel;


if ( ! defined( 'ABSPATH' ) ) {
 exit; // Exit if accessed directly.
}


class AiController extends RequestManager
{

    protected $main_url;
    protected $auth_token = 'spio_ai_jwt_token';

    const AI_STATUS_INVALID_URL = 2;
    const AI_STATUS_OVERQUOTA = 3; 


    public function __construct()
    {
     $this->main_url = 'https://capi-gpt.shortpixel.com/';
    }

    public function processMediaItem(QueueItem $qItem)
    {
      $imageObj = $qItem->imageModel; 
      
      if (! is_object($imageObj))
      {
        $qItem->addResult($this->returnFailure(self::STATUS_FAIL, __('Item seems invalid, removed or corrupted.', 'shortpixel-image-optimiser')));
        return;
      }

      $keyControl = ApiKeyController::getInstance();

      //$request = $this->getRequest($requestArgs);
      $requestBody = [
        'plugin_version' => SHORTPIXEL_IMAGE_OPTIMISER_VERSION,
        'item_id' => $qItem->item_id,
        'source' => 1, // SPIO
      ];

      if ($qItem->data()->action == 'requestAlt')
      {
        $requestBody['url'] = $qItem->data()->urls[0];
        $paramlist = $qItem->data()->paramlist; 
        if (is_object($paramlist))
        {
          $paramlist = (array) $paramlist; 
        }
        if (! is_array($paramlist)) // not serious paramlist then
        {
           $paramlist = []; 
        }

        $requestBody = array_merge($requestBody, $paramlist);
        $requestBody['retry'] = '1'; // when requesting alt, always wants a new one (?) 
        $requestBody['version'] = 'v_2'; 
      }

      if ($qItem->data()->action == 'retrieveAlt')
      {
        $requestBody['id'] = $qItem->data()->remote_id;
      }

      
      $token = get_transient($this->auth_token);
      // Token doesn't seem to work normally.
      /*if ($token !== false)
      {
         $auth = $token; 
      }
      else
      { */
        $auth = 'ApiKey ' . $keyControl->forceGetApiKey();
     // }


      // Should always check the results
      $requestParameters = [
        'blocking' => true,
        'headers' => [
            'Authorization' => $auth,  
            'Content-Type' => 'application/json',
        ]
      ];

      $request = $this->getRequest($requestBody, $requestParameters);
      $this->doRequest($qItem, $request);

    }

    // Should return something that's usefull to set as response on the item.
    protected function handleResponse(QueueItem $qItem, $response)
    {
       $apiData = $this->parseResponse($response);//get the actual response from API, its an array
       Log::addInfo('HAndle AI Response! ', $apiData);

        // List all the random crap that might return. 
        $id = isset($apiData['id']) ? intval($apiData['id']) : false; 
        $jwt = isset($apiData['jwt']) ? sanitize_text_field($apiData['jwt']) : false; 
        $status = isset($apiData['status']) ? intval($apiData['status']) : false;
        
        $error = isset($apiData['error']) ? sanitize_text_field($apiData['error']) : false; 
        $is_error = (false !== $error) ? true : false; 

        if (false !== $jwt)
        {
          $authKey = get_transient($this->auth_token);
          if (false === $authKey || $jwt !== $authKey)
          {
             set_transient($this->auth_token, $jwt, HOUR_IN_SECONDS);
          }

        }

        // @todo This is probably not something that would happen, since repsonse is from the body. Implement here most error coming from the raw request and returnOk/returnFalse etc.

        //if (true === $qItem->result()->is_error && true === $qItem->result()->is_done )
       // {
              /*   So far nothing needed here, documenting what has been seen.
              401 - Unauthorized
              422 - Unprocessable
                */

      
        // API seems to return two different formats : 
        // 1.  requestAlt : Object in data, with ID as only return. 
        // 2.  retrieveAlt: Array with first item ( zero index ) 
        
        if (false === $apiData)
        {
            return $this->returnRetry(RequestManager::STATUS_CONNECTION_ERROR, __('AI Api returned without any data. ', 'shortpixel-image-optimiser')) ;
        }

        if ($qItem->data()->action == 'requestAlt')
        {
            if (false === $id && false === $is_error)
            {
               return $this->returnRetry(RequestManager::STATUS_WAITING, __('Response without result object', 'shortpixel-image-optimiser'));
            }
            
            
            if (false !== $id)
            {
              $remote_id = intval($id);
              $qItem->addResult(['remote_id' => $remote_id]);
              
              return $this->returnSuccess(['remote_id' => $remote_id], RequestManager::STATUS_SUCCESS, __('Request for image SEO data sent to ShortPixel AI', 'shortpixel-image-optimiser'));  
            }
            elseif(self::AI_STATUS_OVERQUOTA === $status)
            {
               return $this->returnFailure(RequestManager::STATUS_ERROR, sprintf(esc_html__('Your AI quota for this month has been exceeded. We would love to hear your feedback — please share it with us %shere%s.', 'shortpixel-image-optimiser'), '<a href="https://shortpixel.com/contact" target="_blank">', '</a>'));
            }
            elseif(self::AI_STATUS_INVALID_URL === $status)
            {
                return $this->returnFailure(RequestManager::STATUS_FAIL, __('No URL or Invalid URL', 'shortpixel-image-optimiser'));
            }
            else
            {
               return $this->returnFailure(RequestManager::STATUS_ERROR, $error);
            }

        }

        if ($qItem->data()->action == 'retrieveAlt')
        {
              $aiData = array_filter([
                 'filename' => isset($apiData['file_name']) ? sanitize_text_field($apiData['file_name']) : null,
                 'alt' => isset($apiData['alt']) ? sanitize_text_field($apiData['alt']) : null, 
                 'caption' => isset($apiData['caption']) ? sanitize_text_field($apiData['caption']) : null, 
                 'relevance' => isset($apiData['relevance']) ? sanitize_text_field($apiData['relevance']) : null, 
                 'description' => isset($apiData['image_description']) ? sanitize_text_field($apiData['image_description']) : null,
                 'post_title' => isset($apiData['title']) ? sanitize_text_field($apiData['title']) : null, 
              ]);              
              
              // Switch known Statii 
              switch ($status)
              {
                  case '-1':  // Error of some kind 
                    $apiStatus = RequestManager::STATUS_FAIL; 
                    return $this->returnFailure($apiStatus, $error); 
                  break; 
                  case '0': // queued
                      if (false !== $is_error)
                      {
                         return $this->returnFailure(RequestManager::STATUS_FAIL, $error);
                      }
                  case '1':
                 
                     return $this->returnOk(RequestManager::STATUS_WAITING, __('Waiting for result', 'shortpixel-image-optimiser'));
                  break; 
                  case '2':  // Success of some kind. 
                  default: 
                      $successData = $this->handleSuccess($aiData, $qItem);
                      return $successData;
                  break;
   
            }
        }
      return $this->returnFailure(0, 'No remote ID?');
    }

    /**
     * Undocumented function
     *
     * @param array $aiData
     * @param object $qItem
     * @return array Result array via requestManager 
     */
    protected function handleSuccess($aiData, QueueItem $qItem)
    {
      if (false === is_null($qItem->data()->returndatalist))
      {
         $returndatalist = $qItem->data()->returndatalist; 
         if (is_object($returndatalist))
         {
           $returndatalist = (array) $returndatalist; 
         }

         foreach($returndatalist as $name => $data)
         {
            if (is_object($data)) // annoying conversion somehow by json decode from record
            {
               $data = (array) $data; 
            }
            if (! isset($aiData[$name]) && isset($data['status']))
            { 
                $aiData[$name]  = $data['status']; 
            }
         }
      }
      
      return $this->returnSuccess(['aiData' => $aiData], RequestManager::STATUS_SUCCESS, __('Retrieved AI image SEO data', 'shortpixel-image-optimiser')); ;
    }

    protected function doRequest(QueueItem $item, $requestParameters)
    {
        // For now
        if (false === property_exists($item->data, 'remote_id') || is_null($item->data()->remote_id))
        {
           $this->apiEndPoint = $this->main_url . 'add-url.php';
        }
        else {
          $this->apiEndPoint = $this->main_url . 'get-url.php';
        }

        return parent::doRequest($item, $requestParameters);

    }

    protected function returnFailure($code, $message)
    {
       if (401 == $code)
       {
          $token = get_transient($this->auth_token);
          if ($token !== false)
          {
             delete_transient($this->auth_token);
             return $this->returnRetry($code, __('Authentication token failure - Reset - Please wait', 'shortpixel-image-optimiser'));
          }
       }
       return parent::returnFailure($code, $message);
    }


}