api_base_url = get_option('aiimatg_api_url', AIIMATG_API_URL); $this->access_token = get_option('aiimatg_access_token'); if (! $this->access_token) { $this->access_token = ''; } } /** * Internal method for logging messages only when debugging is enabled * * @param string $message The message to log * @return void */ private function log($message) { if (defined('WP_DEBUG') && WP_DEBUG) { // Use structured logging if (function_exists('aiimatg_log')) { aiimatg_log($message, 'debug'); } } } /** * Format an object or array for logging * * @param mixed $data The data to format * @return string The formatted string */ private function format_for_log($data) { if (defined('WP_DEBUG') && WP_DEBUG) { // Use json_encode for structured serialization return json_encode($data, JSON_PRETTY_PRINT); } return ''; } /** * Generate alt text via API * * @param array $images Images array to process * @return array|bool API response or false on failure */ public function generate_alt_text($images) { try { if (!is_array($images) || empty($images)) { $this->log('Alt Text Generator API Error: Invalid images array provided'); return array( 'success' => false, 'message' => __('Invalid images data provided', 'ai-image-alt-text-generator') ); } // First check if we have a valid token if (empty($this->access_token)) { $this->log('Alt Text Generator: No access token available'); return array( 'success' => false, 'message' => __('API token is not configured', 'ai-image-alt-text-generator') ); } // Validate API token only for authentication purposes, not for credit limitation if (empty($this->access_token)) { $this->log('Alt Text Generator: Missing API token'); return array( 'success' => false, 'message' => __('API token is required', 'ai-image-alt-text-generator') ); } // Note: Credit limitations are now handled server-side // No client-side credit checking per WordPress.org guidelines // Process the images array foreach ($images as &$image) { if (!isset($image['imageUrl']) || empty($image['imageUrl'])) { $this->log('Alt Text Generator API Error: Image data missing required imageUrl field'); $this->log('Image data: ' . $this->format_for_log($image)); return array( 'success' => false, 'message' => __('Missing required image URL', 'ai-image-alt-text-generator') ); } if (isset($image['type'])) { unset($image['type']); $this->log('Alt Text Generator API: Removed unsupported type field from image data'); } if (isset($image['url'])) { if (!isset($image['imageUrl']) || empty($image['imageUrl'])) { $image['imageUrl'] = $image['url']; $this->log('Alt Text Generator API: Moved url value to imageUrl field'); } unset($image['url']); } } $this->log('Alt Text Generator: Image data for API: ' . $this->format_for_log($images)); $request_data = array( 'service_name' => 'alt_text_generator', 'images' => $images ); $this->log('Alt Text Generator: Final API request data: ' . $this->format_for_log($request_data)); // Add logging for the API endpoint $this->log('Alt Text Generator: Using API endpoint: ' . AIIMATG_ROOT_API_ENDPOINT); $response = wp_remote_post(AIIMATG_ROOT_API_ENDPOINT, array( 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $this->access_token, 'x-api-key' => $this->access_token ), 'body' => json_encode($request_data), 'timeout' => 45 // Increase timeout for larger images )); if (is_wp_error($response)) { $this->log('Alt Text Generator API Error: ' . $response->get_error_message()); return array( 'success' => false, 'message' => $response->get_error_message(), 'error' => 'wp_error' ); } $response_code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $result = json_decode($body, true); $this->log('Alt Text Generator API Response Code: ' . $response_code); $this->log('Alt Text Generator API Response Body: ' . $body); if ($response_code < 200 || $response_code >= 300) { $this->log('Alt Text Generator API HTTP Error: ' . $response_code); $this->log('Alt Text Generator API Response: ' . $body); return array( 'success' => false, 'message' => $this->format_api_error($response_code, $body), 'error' => 'http_error' ); } if (!is_array($result)) { $this->log('Alt Text Generator API Error: Invalid response format'); $this->log('Alt Text Generator API Response: ' . $body); return array( 'success' => false, 'message' => __('Invalid API response format', 'ai-image-alt-text-generator'), 'raw_response' => $body ); } // Update credit information from response if available if (isset($result['data']['total_tokens'])) { // Get current values $credit_spent = get_option('aiimatg_total_credit_spent', 0); $credit_left = get_option('aiimatg_total_credit_left', 0); // Update with new numbers $new_spent = $credit_spent + count($images); $new_left = max(0, $credit_left - count($images)); // Save updated values update_option('aiimatg_total_credit_spent', $new_spent); update_option('aiimatg_total_credit_left', $new_left); $this->log('Alt Text Generator: Updated credit info - Spent: ' . $new_spent . ', Left: ' . $new_left); } // Return the result for processing return $result; } catch (\Exception $e) { $this->log('Alt Text Generator API Error: ' . $e->getMessage()); return array( 'success' => false, 'message' => $e->getMessage(), 'error' => 'exception' ); } } /** * Format common API errors into user-friendly messages * * @param int $code HTTP status code * @param string|array $response Response body or decoded array * @return string Formatted error message */ private function format_api_error($code, $response) { $error_message = __('Unknown API error occurred', 'ai-image-alt-text-generator'); // Format based on HTTP status code switch ($code) { case 401: $error_message = __('API authentication failed. Please check your API token.', 'ai-image-alt-text-generator'); break; case 402: $error_message = __('Service temporarily unavailable. Please try again later.', 'ai-image-alt-text-generator'); break; case 403: $error_message = __('Access forbidden. Your API token does not have permission to access this resource.', 'ai-image-alt-text-generator'); break; case 404: $error_message = __('API endpoint not found. Please contact support.', 'ai-image-alt-text-generator'); break; case 500: case 502: case 503: case 504: $error_message = __('API server error. Please try again later or contact support.', 'ai-image-alt-text-generator'); break; } // Check for specific error messages in the response if (is_string($response)) { $decoded = json_decode($response, true); if ($decoded && is_array($decoded)) { $response = $decoded; } } if (is_array($response)) { // Look for common error patterns in the response if (isset($response['message'])) { $message = $response['message']; // Look for common error messages and provide more helpful responses if (stripos($message, 'limit') !== false && (stripos($message, 'exceed') !== false || stripos($message, 'reach') !== false)) { $error_message = __('Service temporarily unavailable. Please try again later.', 'ai-image-alt-text-generator'); } else if (stripos($message, 'invalid') !== false && stripos($message, 'token') !== false) { $error_message = __('Invalid API token. Please check your settings and try again.', 'ai-image-alt-text-generator'); } else if (stripos($message, 'expire') !== false) { $error_message = __('Your API token has expired. Please renew your subscription.', 'ai-image-alt-text-generator'); } else if (strlen($message) > 10) { // If there's a substantial message, use it $error_message = $message; } } else if (isset($response['error']) && isset($response['error']['message'])) { $error_message = $response['error']['message']; } } return $error_message; } /** * Process API response into standardized format for the plugin * * @param array|bool $result API response or false on failure * @return array Processed result */ public function process_api_result($result) { // Default response structure $processed_result = array( 'success' => false, 'message' => __('Failed to generate alt text', 'ai-image-alt-text-generator'), 'formatted' => array(), 'updated' => array() ); // Handle complete failure cases if ($result === false || !is_array($result)) { $this->log('API Client: Response processing failed - Invalid response'); return $processed_result; } // Log the original response structure for debugging $this->log('API Client: Processing response: ' . $this->format_for_log($result)); // Special handling for response structure inconsistencies if (isset($result['success']) && $result['success'] === false) { // Check if there's still valid data despite the success flag being false if (isset($result['data']) && is_array($result['data']) && isset($result['data']['message'])) { $message = $result['data']['message']; if (strpos($message, 'successfully') !== false) { // This appears to be a successful response despite the success flag $this->log('API Client: Response has success=false but message indicates success. Treating as success.'); // Continue processing as if success was true } else { // This is a genuine error response $processed_result['message'] = $message; $this->log('API Client: API error - ' . $message); return $processed_result; } } else { // Standard error response $message = isset($result['message']) ? $result['message'] : __('API returned an error', 'ai-image-alt-text-generator'); $processed_result['message'] = $message; $this->log('API Client: API error - ' . $message); return $processed_result; } } // Check if we have the nested data structure $images_data = array(); $updated_ids = array(); $total_tokens = 0; $image_count = 0; // Try to extract data from both standard and inconsistent response structures if (isset($result['data']) && is_array($result['data'])) { if (isset($result['data']['data']) && is_array($result['data']['data'])) { // Standard nested structure $images_data = $result['data']['data']; if (isset($result['data']['total_tokens'])) { $total_tokens = intval($result['data']['total_tokens']); } if (isset($result['data']['image_count'])) { $image_count = intval($result['data']['image_count']); } } else if (isset($result['data'][0]) && is_array($result['data'][0])) { // Alternative structure where data is directly an array of images $images_data = $result['data']; $this->log('API Client: Found alternative data structure - direct array'); } } // If we still have no image data, check one more structure if (empty($images_data) && isset($result['data']) && isset($result['data']['altText'])) { // Single image response structure $images_data = array($result['data']); $this->log('API Client: Found single image response structure'); } // Process each image result $formatted_results = array(); foreach ($images_data as $image) { if (isset($image['id'])) { $alt_text = isset($image['altText']) ? $image['altText'] : (isset($image['alt_text']) ? $image['alt_text'] : ''); if (!empty($alt_text)) { $formatted_results[] = array( 'id' => $image['id'], 'altText' => $alt_text ); $updated_ids[] = $image['id']; // Update the alt text in WordPress $attachment_id = absint($image['id']); if ($attachment_id > 0) { update_post_meta($attachment_id, '_wp_attachment_image_alt', wp_strip_all_tags($alt_text)); $this->log('API Client: Updated alt text for image ID: ' . $attachment_id); } } } } // If we successfully processed any images, consider this a success if (!empty($updated_ids)) { $processed_result = array( 'success' => true, 'message' => __('Alt text generated successfully', 'ai-image-alt-text-generator'), 'formatted' => $formatted_results, 'updated' => $updated_ids, 'total_tokens' => $total_tokens, 'image_count' => $image_count ); } $this->log('API Client: Processed result: ' . $this->format_for_log($processed_result)); return $processed_result; } /** * Validate the API token * * @return array Results of validation with success status */ public function validate_token() { if (empty($this->access_token)) { return [ 'success' => false, 'message' => __('API token is empty', 'ai-image-alt-text-generator') ]; } try { $this->log('Validating API token: ' . substr($this->access_token, 0, 5) . '...'); // Prepare the API request with POST method as shown in the API documentation // Make sure to use the ROOT API endpoint as defined in the plugin constants $endpoint = AIIMATG_AUTH_ENDPOINT; $this->log('Using API endpoint: ' . $endpoint); // Set up the request with POST method and the correct JSON structure $args = array( 'method' => 'POST', 'timeout' => 45, 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $this->access_token, 'x-api-key' => $this->access_token ), 'body' => json_encode(array( 'service_name' => 'alt_text_generator', 'api_key' => $this->access_token )) ); $this->log('API token validation request: ' . $this->format_for_log($args)); // Make the request $response = wp_remote_request($endpoint, $args); // Check if the request was successful if (is_wp_error($response)) { $this->log('API token validation error: ' . $response->get_error_message()); return [ 'success' => false, 'message' => $response->get_error_message() ]; } $status_code = wp_remote_retrieve_response_code($response); $response_body = wp_remote_retrieve_body($response); $body = json_decode($response_body, true); $this->log('API token validation response status: ' . $status_code); $this->log('API token validation raw response: ' . $response_body); $this->log('API token validation parsed response: ' . $this->format_for_log($body)); // Return the full API response for successful validation if ($status_code >= 200 && $status_code < 300 && isset($body['success']) && $body['success']) { return [ 'success' => true, 'message' => isset($body['message']) ? $body['message'] : __('API token is valid', 'ai-image-alt-text-generator'), 'data' => isset($body['data']) ? $body['data'] : [] ]; } else { $message = isset($body['message']) ? $body['message'] : __('API token validation failed', 'ai-image-alt-text-generator'); return [ 'success' => false, 'message' => $message ]; } } catch (\Exception $e) { $this->log('Exception during API token validation: ' . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } }