$method, 'timeout' => $timeout, 'redirection' => 0, 'sslverify' => true, 'blocking' => true, 'headers' => array_merge( array( 'Accept' => 'application/json', 'User-Agent' => 'AI House/' . MYBOT_VERSION . '; ' . home_url(), ), $headers ), ); if ( null !== $body ) { $args['body'] = $body; $args['headers']['Content-Type'] = 'application/json'; } while ( true ) { ++$attempt; $response = wp_remote_request( $url, $args ); if ( is_wp_error( $response ) ) { if ( $attempt <= $retries ) { self::backoff( $attempt ); continue; } return new WP_Error( 'mybot_http_error', $response->get_error_message(), array( 'attempts' => $attempt ) ); } $code = (int) wp_remote_retrieve_response_code( $response ); $raw = wp_remote_retrieve_body( $response ); if ( $code >= 500 && $attempt <= $retries ) { self::backoff( $attempt ); continue; } $decoded = json_decode( $raw, true ); if ( $code < 200 || $code >= 300 ) { return new WP_Error( 'mybot_http_status', self::extract_error_message( $decoded, $code ), array( 'status' => $code, 'response' => $decoded, ) ); } if ( null === $decoded && '' !== trim( (string) $raw ) ) { return new WP_Error( 'mybot_http_json', __( 'The response is not valid JSON.', 'aihouse' ) ); } return is_array( $decoded ) ? $decoded : array(); } } /** * Extracts a readable error message from the response. * * @param mixed $decoded Decoded data. * @param int $code HTTP status code. * @return string */ protected static function extract_error_message( $decoded, $code ) { if ( is_array( $decoded ) ) { if ( isset( $decoded['error']['message'] ) ) { return (string) $decoded['error']['message']; } if ( isset( $decoded['message'] ) && is_string( $decoded['message'] ) ) { return $decoded['message']; } if ( isset( $decoded['error'] ) && is_string( $decoded['error'] ) ) { return $decoded['error']; } } /* translators: %d: HTTP status code */ return sprintf( __( 'The provider returned HTTP %d.', 'aihouse' ), $code ); } /** * Waits with exponential backoff before retrying. * * @param int $attempt Current attempt number. * @return void */ protected static function backoff( $attempt ) { $microseconds = (int) ( 250000 * pow( 2, max( 0, $attempt - 1 ) ) ); // Only used in background tasks, never on the hot path. usleep( min( $microseconds, 2000000 ) ); } }