api = new Bluesky_API($access_token, $did); } public function format_and_post($post) { $content = $this->get_formatted_content($post); $post_data = array( '$type' => 'app.bsky.feed.post', 'text' => $content, 'createdAt' => gmdate('c', strtotime($post->post_date_gmt)), 'langs' => array('en') ); // Add facets for the URL $post_data['facets'] = $this->parse_facets($post); // Handle image embed separately from text content if (has_post_thumbnail($post->ID)) { $image_data = $this->handle_featured_image($post->ID); if (!empty($image_data) && !isset($image_data['error'])) { $post_data['embed'] = $image_data; } } return $this->api->create_post($post_data); } private function get_formatted_content($post) { $format = get_option('bluesky_post_format', 'image-title-excerpt-link'); $include_title = get_option('bluesky_include_title', true); // Get individual components $title = $include_title ? $post->post_title : ''; $excerpt = $this->get_excerpt($post); $url = wp_get_shortlink($post->ID); // Start building content with explicit line breaks $content = ''; // Add title with line break if it exists if (!empty($title)) { $content .= $title . "\n\n"; } // Add excerpt if it exists if (!empty($excerpt)) { $content .= $excerpt . "\n\n"; // Add double line break after excerpt } // Add URL on its own line if (!empty($url)) { $content .= $url; // URL starts on new line due to previous \n\n } return $content; } private function get_excerpt($post) { $text = wp_strip_all_tags($post->post_excerpt); if (empty($text)) { $text = wp_strip_all_tags($post->post_content); } $url = wp_get_shortlink($post->ID); $include_title = get_option('bluesky_include_title', true); // Calculate available length accounting for spacing and new lines $available_length = $this->max_length; $available_length -= strlen($url); $available_length -= 2; // Account for \n\n after excerpt if ($include_title) { $available_length -= strlen($post->post_title); $available_length -= 2; // Account for \n\n after title } if (mb_strlen($text) > $available_length) { $text = mb_substr($text, 0, $available_length - 3) . '...'; } return $text; } private function parse_facets($post) { $facets = array(); $content = $this->get_formatted_content($post); // Add link facet for the post URL $url = wp_get_shortlink($post->ID); $text_bytes = mb_convert_encoding($content, 'UTF-8'); $url_position = mb_strrpos($text_bytes, $url); if ($url_position !== false) { $facets[] = array( 'index' => array( 'byteStart' => $url_position, 'byteEnd' => $url_position + strlen($url), ), 'features' => array( array( '$type' => 'app.bsky.richtext.facet#link', 'uri' => $url, ), ), ); } return $facets; } private function handle_featured_image($post_id) { $image_id = get_post_thumbnail_id($post_id); $image_path = get_attached_file($image_id); if (!$image_path) { return array('error' => 'Image file not found'); } $mime_type = get_post_mime_type($image_id); // Get image data $image_data = file_get_contents($image_path); if ($image_data === false) { return array('error' => 'Failed to read image file'); } // Check file size (1MB limit for Bluesky) if (strlen($image_data) > 1000000) { // If image is too large, attempt to resize it $resized = $this->resize_image($image_path); if ($resized) { $image_data = file_get_contents($resized); unlink($resized); // Clean up temporary file } else { return array('error' => 'Image file size exceeds 1MB limit and resize failed'); } } // Upload image blob $response = $this->api->upload_blob($image_path, $mime_type); if (isset($response['error'])) { return $response; } // Get the alt text $alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true) ?: ''; return array( '$type' => 'app.bsky.embed.images', 'images' => array( array( 'alt' => $alt_text, 'image' => $response['blob'], ), ), ); } private function resize_image($image_path) { // Only proceed if GD is available if (!function_exists('imagecreatefrompng')) { return false; } $mime_type = mime_content_type($image_path); list($width, $height) = getimagesize($image_path); // Calculate new dimensions while maintaining aspect ratio $max_dimension = 1000; // Reasonable size that should result in < 1MB file if ($width > $height) { $new_width = $max_dimension; $new_height = floor($height * ($max_dimension / $width)); } else { $new_height = $max_dimension; $new_width = floor($width * ($max_dimension / $height)); } // Create new image $new_image = imagecreatetruecolor($new_width, $new_height); // Handle different image types switch ($mime_type) { case 'image/jpeg': $source = imagecreatefromjpeg($image_path); break; case 'image/png': $source = imagecreatefrompng($image_path); // Preserve transparency imagealphablending($new_image, false); imagesavealpha($new_image, true); break; case 'image/gif': $source = imagecreatefromgif($image_path); break; default: return false; } if (!$source) { return false; } // Resize imagecopyresampled( $new_image, $source, 0, 0, 0, 0, $new_width, $new_height, $width, $height ); // Create temporary file $temp_file = tempnam(sys_get_temp_dir(), 'bluesky_img_'); // Save resized image switch ($mime_type) { case 'image/jpeg': imagejpeg($new_image, $temp_file, 85); break; case 'image/png': imagepng($new_image, $temp_file, 8); break; case 'image/gif': imagegif($new_image, $temp_file); break; } // Clean up imagedestroy($source); imagedestroy($new_image); return $temp_file; } }