Skip to content

Commit e06e65e

Browse files
committed
feat: add SocialCrossPostTask for async cross-posting via DM Task System
New task type 'social_cross_post' registered via datamachine_tasks filter. Fires asynchronously via Action Scheduler when a post is published with social platform targets. Results logged to post meta and DM job engine_data. Visible in wp datamachine jobs list, retryable via wp datamachine jobs retry.
1 parent 49587d1 commit e06e65e

2 files changed

Lines changed: 155 additions & 0 deletions

File tree

data-machine-socials.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ function datamachine_socials_bootstrap() {
110110
// Reddit (Fetch)
111111
new \DataMachineSocials\Handlers\Reddit\Reddit();
112112

113+
// Register task handlers for DM Task System.
114+
add_filter( 'datamachine_tasks', function ( array $tasks ): array {
115+
$tasks['social_cross_post'] = \DataMachineSocials\Tasks\SocialCrossPostTask::class;
116+
return $tasks;
117+
} );
118+
113119
// Register image generation templates
114120
add_filter( 'datamachine/image_generation/templates', function ( array $templates ): array {
115121
$templates['quote_card'] = \DataMachineSocials\ImageGeneration\Templates\QuoteCard::class;

inc/Tasks/SocialCrossPostTask.php

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
/**
3+
* Social Cross-Post Task for Data Machine Task System.
4+
*
5+
* Async cross-posting of published content to social platforms.
6+
* Fires via Action Scheduler after a post transitions to 'publish'.
7+
* Each platform is posted sequentially within a single job.
8+
*
9+
* @package DataMachineSocials\Tasks
10+
* @since 0.9.0
11+
*/
12+
13+
namespace DataMachineSocials\Tasks;
14+
15+
defined( 'ABSPATH' ) || exit;
16+
17+
use DataMachine\Engine\AI\System\Tasks\SystemTask;
18+
19+
class SocialCrossPostTask extends SystemTask {
20+
21+
/**
22+
* Execute the cross-post for a specific job.
23+
*
24+
* @param int $jobId Job ID from DM Jobs table.
25+
* @param array $params Task parameters from engine_data.
26+
*/
27+
public function execute( int $jobId, array $params ): void {
28+
$post_id = absint( $params['post_id'] ?? 0 );
29+
$platforms = $params['platforms'] ?? array();
30+
$caption = $params['caption'] ?? '';
31+
$images = $params['images'] ?? array();
32+
$media_kind = $params['media_kind'] ?? 'image';
33+
$aspect_ratio = $params['aspect_ratio'] ?? '4:5';
34+
$video_url = $params['video_url'] ?? '';
35+
$cover_url = $params['cover_url'] ?? '';
36+
37+
if ( $post_id <= 0 ) {
38+
$this->failJob( $jobId, 'Missing or invalid post_id' );
39+
return;
40+
}
41+
42+
if ( empty( $platforms ) || ! is_array( $platforms ) ) {
43+
$this->failJob( $jobId, 'No platforms specified' );
44+
return;
45+
}
46+
47+
if ( empty( $caption ) ) {
48+
$this->failJob( $jobId, 'Empty caption' );
49+
return;
50+
}
51+
52+
// Call the cross-post REST endpoint internally.
53+
$request = new \WP_REST_Request( 'POST', '/datamachine-socials/v1/post' );
54+
$request->set_header( 'Content-Type', 'application/json' );
55+
$request->set_body( wp_json_encode( array(
56+
'platforms' => $platforms,
57+
'caption' => $caption,
58+
'images' => $images,
59+
'aspect_ratio' => $aspect_ratio,
60+
'media_kind' => $media_kind,
61+
'video_url' => $video_url,
62+
'cover_url' => $cover_url,
63+
'post_id' => $post_id,
64+
'share_to_feed' => true,
65+
) ) );
66+
67+
$response = rest_do_request( $request );
68+
$data = $response->get_data();
69+
70+
// Build per-platform result log.
71+
$log = array();
72+
$failures = array();
73+
74+
if ( isset( $data['results'] ) && is_array( $data['results'] ) ) {
75+
foreach ( $data['results'] as $result ) {
76+
$entry = array(
77+
'platform' => $result['platform'] ?? 'unknown',
78+
'success' => $result['success'] ?? false,
79+
'post_id' => $result['platform_post_id'] ?? '',
80+
'url' => $result['platform_url'] ?? '',
81+
'error' => $result['error'] ?? '',
82+
'timestamp' => gmdate( 'c' ),
83+
);
84+
$log[] = $entry;
85+
86+
if ( empty( $result['success'] ) ) {
87+
$failures[] = ( $result['platform'] ?? 'unknown' ) . ': ' . ( $result['error'] ?? 'Unknown error' );
88+
}
89+
}
90+
} else {
91+
$log[] = array(
92+
'platform' => 'system',
93+
'success' => false,
94+
'error' => $data['error'] ?? 'Cross-post API returned unexpected response.',
95+
'timestamp' => gmdate( 'c' ),
96+
);
97+
$failures[] = $data['error'] ?? 'Unexpected response';
98+
}
99+
100+
// Store results in post meta.
101+
$existing_log = get_post_meta( $post_id, '_studio_social_publish_log', true ) ?: array();
102+
$merged_log = array_merge( $existing_log, $log );
103+
update_post_meta( $post_id, '_studio_social_publish_log', $merged_log );
104+
105+
// Complete or fail the job.
106+
$successes = array_filter( $log, function ( $entry ) {
107+
return ! empty( $entry['success'] );
108+
} );
109+
110+
if ( empty( $successes ) ) {
111+
$this->failJob( $jobId, 'All platforms failed: ' . implode( '; ', $failures ) );
112+
return;
113+
}
114+
115+
$this->completeJob( $jobId, array(
116+
'post_id' => $post_id,
117+
'platforms' => $platforms,
118+
'results' => $log,
119+
'success_count' => count( $successes ),
120+
'failure_count' => count( $failures ),
121+
) );
122+
}
123+
124+
/**
125+
* Get the task type identifier.
126+
*
127+
* @return string
128+
*/
129+
public function getTaskType(): string {
130+
return 'social_cross_post';
131+
}
132+
133+
/**
134+
* Get task metadata for UI display.
135+
*
136+
* @return array
137+
*/
138+
public static function getTaskMeta(): array {
139+
return array(
140+
'label' => __( 'Social Cross-Post', 'data-machine-socials' ),
141+
'description' => __( 'Cross-posts published content to social platforms via Data Machine Socials.', 'data-machine-socials' ),
142+
'setting_key' => null,
143+
'default_enabled' => true,
144+
'trigger' => 'Post publish',
145+
'trigger_type' => 'event',
146+
'supports_run' => true,
147+
);
148+
}
149+
}

0 commit comments

Comments
 (0)