type === 'twitter'; } public function publish(Post $post, ?SocialAccount $socialAccount = null): PublishResult { if (! $post->body) { return PublishResult::rejected("Post {$post->id} has no body, required for a Twitter post."); } // Use the passed social account, or fall back to the first linked one $account = $socialAccount ?: $post->socialAccounts->first(); if (! $account || $account->network !== 'twitter') { return PublishResult::rejected("Post {$post->id} has no linked Twitter social account."); } // Twitter uses username (which may be email or username) and password $result = Process::timeout(120)->run([ base_path('sidecars/twitter-post/run.sh'), "--username={$account->username}", "--password={$account->password}", "--tweet=" . escapeshellarg($post->body), ]); $data = json_decode(trim($result->output()), true); if (! is_array($data) || ! array_key_exists('success', $data)) { Log::error('twitter sidecar returned unparseable output', [ 'post_id' => $post->id, 'output' => $result->output(), 'stderr' => $result->errorOutput(), ]); return PublishResult::failed('Twitter sidecar returned unparseable output — check logs.'); } if (! $data['success']) { return PublishResult::failed($data['error'] ?? 'unknown Twitter sidecar failure'); } return PublishResult::success($data['url']); } }